diff --git a/.changelog.yml b/.changelog.yml
index bfdee0c0ca..a7df8779de 100644
--- a/.changelog.yml
+++ b/.changelog.yml
@@ -22,20 +22,25 @@ groups:
name: FEATURES
labels:
- type/feature
- -
- name: API
- labels:
- - modifies/api
-
name: ENHANCEMENTS
labels:
- type/enhancement
- - type/refactoring
- - topic/ui
+ -
+ name: PERFORMANCE
+ labels:
+ - performance/memory
+ - performance/speed
+ - performance/bigrepo
+ - performance/cpu
-
name: BUGFIXES
labels:
- type/bug
+ -
+ name: API
+ labels:
+ - modifies/api
-
name: TESTING
labels:
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 9f9f6f27d1..104f1cfeed 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,13 +1,13 @@
{
"name": "Gitea DevContainer",
- "image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm",
+ "image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
"features": {
// installs nodejs into container
"ghcr.io/devcontainers/features/node:1": {
- "version": "20"
+ "version": "lts"
},
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
- "ghcr.io/devcontainers-contrib/features/poetry:2": {},
+ "ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.12"
},
diff --git a/.dockerignore b/.dockerignore
index b696e1603c..843f12a7be 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -36,15 +36,6 @@ _testmain.go
coverage.all
cpu.out
-/modules/migration/bindata.go
-/modules/migration/bindata.go.hash
-/modules/options/bindata.go
-/modules/options/bindata.go.hash
-/modules/public/bindata.go
-/modules/public/bindata.go.hash
-/modules/templates/bindata.go
-/modules/templates/bindata.go.hash
-
*.db
*.log
@@ -79,18 +70,6 @@ cpu.out
/public/assets/fonts
/public/assets/img/avatar
/vendor
-/web_src/fomantic/node_modules
-/web_src/fomantic/build/*
-!/web_src/fomantic/build/semantic.js
-!/web_src/fomantic/build/semantic.css
-!/web_src/fomantic/build/themes
-/web_src/fomantic/build/themes/*
-!/web_src/fomantic/build/themes/default
-/web_src/fomantic/build/themes/default/assets/*
-!/web_src/fomantic/build/themes/default/assets/fonts
-/web_src/fomantic/build/themes/default/assets/fonts/*
-!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
-!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION
/.air
/.go-licenses
diff --git a/.editorconfig b/.editorconfig
index c0946ac997..13aa8d50f0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,11 +12,15 @@ insert_final_newline = true
[*.{go,tmpl,html}]
indent_style = tab
+[go.*]
+indent_style = tab
+
[templates/custom/*.tmpl]
insert_final_newline = false
[templates/swagger/v1_json.tmpl]
indent_style = space
+insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl]
indent_style = space
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000000..f9e1050240
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,1004 @@
+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: {
+ '@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-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': [0],
+ 'grouped-accessor-pairs': [2],
+ 'guard-for-in': [0],
+ 'id-blacklist': [0],
+ 'id-length': [0],
+ 'id-match': [0],
+ 'import-x/consistent-type-specifier-style': [0],
+ 'import-x/default': [0],
+ 'import-x/dynamic-import-chunkname': [0],
+ 'import-x/export': [2],
+ 'import-x/exports-last': [0],
+ 'import-x/extensions': [2, 'always', {ignorePackages: true}],
+ 'import-x/first': [2],
+ 'import-x/group-exports': [0],
+ 'import-x/max-dependencies': [0],
+ 'import-x/named': [2],
+ 'import-x/namespace': [0],
+ 'import-x/newline-after-import': [0],
+ 'import-x/no-absolute-path': [0],
+ 'import-x/no-amd': [2],
+ 'import-x/no-anonymous-default-export': [0],
+ 'import-x/no-commonjs': [2],
+ 'import-x/no-cycle': [2, {ignoreExternal: true, maxDepth: 1}],
+ 'import-x/no-default-export': [0],
+ 'import-x/no-deprecated': [0],
+ 'import-x/no-dynamic-require': [0],
+ 'import-x/no-empty-named-blocks': [2],
+ 'import-x/no-extraneous-dependencies': [2],
+ 'import-x/no-import-module-exports': [0],
+ 'import-x/no-internal-modules': [0],
+ 'import-x/no-mutable-exports': [0],
+ 'import-x/no-named-as-default-member': [0],
+ 'import-x/no-named-as-default': [0],
+ 'import-x/no-named-default': [0],
+ 'import-x/no-named-export': [0],
+ 'import-x/no-namespace': [0],
+ 'import-x/no-nodejs-modules': [0],
+ 'import-x/no-relative-packages': [0],
+ 'import-x/no-relative-parent-imports': [0],
+ 'import-x/no-restricted-paths': [0],
+ 'import-x/no-self-import': [2],
+ 'import-x/no-unassigned-import': [0],
+ 'import-x/no-unresolved': [2, {commonjs: true, ignore: ['\\?.+$']}],
+ 'import-x/no-unused-modules': [2, {unusedExports: true}],
+ 'import-x/no-useless-path-segments': [2, {commonjs: true}],
+ 'import-x/no-webpack-loader-syntax': [2],
+ 'import-x/order': [0],
+ 'import-x/prefer-default-export': [0],
+ 'import-x/unambiguous': [0],
+ 'init-declarations': [0],
+ 'line-comment-position': [0],
+ 'logical-assignment-operators': [0],
+ 'max-classes-per-file': [0],
+ 'max-depth': [0],
+ 'max-lines-per-function': [0],
+ 'max-lines': [0],
+ 'max-nested-callbacks': [0],
+ 'max-params': [0],
+ 'max-statements': [0],
+ 'multiline-comment-style': [2, 'separate-lines'],
+ 'new-cap': [0],
+ 'no-alert': [0],
+ 'no-array-constructor': [0], // handled by @typescript-eslint/no-array-constructor
+ 'no-async-promise-executor': [0],
+ 'no-await-in-loop': [0],
+ 'no-bitwise': [0],
+ 'no-buffer-constructor': [0],
+ 'no-caller': [2],
+ 'no-case-declarations': [2],
+ 'no-class-assign': [2],
+ 'no-compare-neg-zero': [2],
+ 'no-cond-assign': [2, 'except-parens'],
+ 'no-console': [1, {allow: ['debug', 'info', 'warn', 'error']}],
+ 'no-const-assign': [2],
+ 'no-constant-binary-expression': [2],
+ 'no-constant-condition': [0],
+ 'no-constructor-return': [2],
+ 'no-continue': [0],
+ 'no-control-regex': [0],
+ 'no-debugger': [1],
+ 'no-delete-var': [2],
+ 'no-div-regex': [0],
+ 'no-dupe-args': [2],
+ 'no-dupe-class-members': [2],
+ 'no-dupe-else-if': [2],
+ 'no-dupe-keys': [2],
+ 'no-duplicate-case': [2],
+ 'no-duplicate-imports': [0],
+ 'no-else-return': [2],
+ 'no-empty-character-class': [2],
+ 'no-empty-function': [0],
+ 'no-empty-pattern': [2],
+ 'no-empty-static-block': [2],
+ 'no-empty': [2, {allowEmptyCatch: true}],
+ 'no-eq-null': [2],
+ 'no-eval': [2],
+ 'no-ex-assign': [2],
+ 'no-extend-native': [2],
+ 'no-extra-bind': [2],
+ 'no-extra-boolean-cast': [2],
+ 'no-extra-label': [0],
+ 'no-fallthrough': [2],
+ 'no-func-assign': [2],
+ 'no-global-assign': [2],
+ 'no-implicit-coercion': [2],
+ 'no-implicit-globals': [0],
+ 'no-implied-eval': [0], // handled by @typescript-eslint/no-implied-eval
+ 'no-import-assign': [2],
+ 'no-inline-comments': [0],
+ 'no-inner-declarations': [2],
+ 'no-invalid-regexp': [2],
+ 'no-invalid-this': [0],
+ 'no-irregular-whitespace': [2],
+ 'no-iterator': [2],
+ 'no-jquery/no-ajax-events': [2],
+ 'no-jquery/no-ajax': [2],
+ 'no-jquery/no-and-self': [2],
+ 'no-jquery/no-animate-toggle': [2],
+ 'no-jquery/no-animate': [2],
+ 'no-jquery/no-append-html': [2],
+ 'no-jquery/no-attr': [2],
+ 'no-jquery/no-bind': [2],
+ 'no-jquery/no-box-model': [2],
+ 'no-jquery/no-browser': [2],
+ 'no-jquery/no-camel-case': [2],
+ 'no-jquery/no-class-state': [2],
+ 'no-jquery/no-class': [0],
+ 'no-jquery/no-clone': [2],
+ 'no-jquery/no-closest': [0],
+ 'no-jquery/no-constructor-attributes': [2],
+ 'no-jquery/no-contains': [2],
+ 'no-jquery/no-context-prop': [2],
+ 'no-jquery/no-css': [2],
+ 'no-jquery/no-data': [0],
+ 'no-jquery/no-deferred': [2],
+ 'no-jquery/no-delegate': [2],
+ 'no-jquery/no-done-fail': [2],
+ 'no-jquery/no-each-collection': [0],
+ 'no-jquery/no-each-util': [0],
+ 'no-jquery/no-each': [0],
+ 'no-jquery/no-error-shorthand': [2],
+ 'no-jquery/no-error': [2],
+ 'no-jquery/no-escape-selector': [2],
+ 'no-jquery/no-event-shorthand': [2],
+ 'no-jquery/no-extend': [2],
+ 'no-jquery/no-fade': [2],
+ 'no-jquery/no-filter': [0],
+ 'no-jquery/no-find-collection': [0],
+ 'no-jquery/no-find-util': [2],
+ 'no-jquery/no-find': [0],
+ 'no-jquery/no-fx-interval': [2],
+ 'no-jquery/no-fx': [2],
+ 'no-jquery/no-global-eval': [2],
+ 'no-jquery/no-global-selector': [0],
+ 'no-jquery/no-grep': [2],
+ 'no-jquery/no-has': [2],
+ 'no-jquery/no-hold-ready': [2],
+ 'no-jquery/no-html': [0],
+ 'no-jquery/no-in-array': [2],
+ 'no-jquery/no-is-array': [2],
+ 'no-jquery/no-is-empty-object': [2],
+ 'no-jquery/no-is-function': [2],
+ 'no-jquery/no-is-numeric': [2],
+ 'no-jquery/no-is-plain-object': [2],
+ 'no-jquery/no-is-window': [2],
+ 'no-jquery/no-is': [2],
+ 'no-jquery/no-jquery-constructor': [0],
+ 'no-jquery/no-live': [2],
+ 'no-jquery/no-load-shorthand': [2],
+ 'no-jquery/no-load': [2],
+ 'no-jquery/no-map-collection': [0],
+ 'no-jquery/no-map-util': [2],
+ 'no-jquery/no-map': [2],
+ 'no-jquery/no-merge': [2],
+ 'no-jquery/no-node-name': [2],
+ 'no-jquery/no-noop': [2],
+ 'no-jquery/no-now': [2],
+ 'no-jquery/no-on-ready': [2],
+ 'no-jquery/no-other-methods': [0],
+ 'no-jquery/no-other-utils': [2],
+ 'no-jquery/no-param': [2],
+ 'no-jquery/no-parent': [0],
+ 'no-jquery/no-parents': [2],
+ 'no-jquery/no-parse-html-literal': [2],
+ 'no-jquery/no-parse-html': [2],
+ 'no-jquery/no-parse-json': [2],
+ 'no-jquery/no-parse-xml': [2],
+ 'no-jquery/no-prop': [2],
+ 'no-jquery/no-proxy': [2],
+ 'no-jquery/no-ready-shorthand': [2],
+ 'no-jquery/no-ready': [2],
+ 'no-jquery/no-selector-prop': [2],
+ 'no-jquery/no-serialize': [2],
+ 'no-jquery/no-size': [2],
+ 'no-jquery/no-sizzle': [2],
+ 'no-jquery/no-slide': [2],
+ 'no-jquery/no-sub': [2],
+ 'no-jquery/no-support': [2],
+ 'no-jquery/no-text': [2],
+ 'no-jquery/no-trigger': [0],
+ 'no-jquery/no-trim': [2],
+ 'no-jquery/no-type': [2],
+ 'no-jquery/no-unique': [2],
+ 'no-jquery/no-unload-shorthand': [2],
+ 'no-jquery/no-val': [0],
+ 'no-jquery/no-visibility': [2],
+ 'no-jquery/no-when': [2],
+ 'no-jquery/no-wrap': [2],
+ 'no-jquery/variable-pattern': [2],
+ 'no-label-var': [2],
+ 'no-labels': [0], // handled by no-restricted-syntax
+ 'no-lone-blocks': [2],
+ 'no-lonely-if': [0],
+ 'no-loop-func': [0],
+ 'no-loss-of-precision': [2],
+ 'no-magic-numbers': [0],
+ 'no-misleading-character-class': [2],
+ 'no-multi-assign': [0],
+ 'no-multi-str': [2],
+ 'no-negated-condition': [0],
+ 'no-nested-ternary': [0],
+ 'no-new-func': [2],
+ 'no-new-native-nonconstructor': [2],
+ 'no-new-object': [2],
+ 'no-new-symbol': [2],
+ 'no-new-wrappers': [2],
+ 'no-new': [0],
+ 'no-nonoctal-decimal-escape': [2],
+ 'no-obj-calls': [2],
+ 'no-octal-escape': [2],
+ 'no-octal': [2],
+ 'no-param-reassign': [0],
+ 'no-plusplus': [0],
+ 'no-promise-executor-return': [0],
+ 'no-proto': [2],
+ 'no-prototype-builtins': [2],
+ 'no-redeclare': [0], // must be disabled for typescript overloads
+ 'no-regex-spaces': [2],
+ 'no-restricted-exports': [0],
+ 'no-restricted-globals': [2, 'addEventListener', 'blur', 'close', 'closed', 'confirm', 'defaultStatus', 'defaultstatus', 'error', 'event', 'external', 'find', 'focus', 'frameElement', 'frames', 'history', 'innerHeight', 'innerWidth', 'isFinite', 'isNaN', 'length', 'locationbar', 'menubar', 'moveBy', 'moveTo', 'name', 'onblur', 'onerror', 'onfocus', 'onload', 'onresize', 'onunload', 'open', 'opener', 'opera', 'outerHeight', 'outerWidth', 'pageXOffset', 'pageYOffset', 'parent', 'print', 'removeEventListener', 'resizeBy', 'resizeTo', 'screen', 'screenLeft', 'screenTop', 'screenX', 'screenY', 'scroll', 'scrollbars', 'scrollBy', 'scrollTo', 'scrollX', 'scrollY', 'status', 'statusbar', 'stop', 'toolbar', 'top'],
+ 'no-restricted-imports': [0],
+ 'no-restricted-syntax': [2, ...restrictedSyntax, {selector: 'CallExpression[callee.name="fetch"]', message: 'use modules/fetch.ts instead'}],
+ 'no-return-assign': [0],
+ 'no-script-url': [2],
+ 'no-self-assign': [2, {props: true}],
+ 'no-self-compare': [2],
+ 'no-sequences': [2],
+ 'no-setter-return': [2],
+ 'no-shadow-restricted-names': [2],
+ 'no-shadow': [0],
+ 'no-sparse-arrays': [2],
+ 'no-template-curly-in-string': [2],
+ 'no-ternary': [0],
+ 'no-this-before-super': [2],
+ 'no-throw-literal': [2],
+ 'no-undef-init': [2],
+ 'no-undef': [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-one-iteration-loop': [2],
+ 'sonarjs/no-redundant-boolean': [2],
+ 'sonarjs/no-redundant-jump': [2],
+ 'sonarjs/no-same-line-conditional': [2],
+ 'sonarjs/no-small-switch': [0],
+ 'sonarjs/no-unused-collection': [2],
+ 'sonarjs/no-use-of-empty-return-value': [2],
+ 'sonarjs/no-useless-catch': [2],
+ 'sonarjs/non-existent-operator': [2],
+ 'sonarjs/prefer-immediate-return': [0],
+ 'sonarjs/prefer-object-literal': [0],
+ 'sonarjs/prefer-single-boolean-return': [0],
+ 'sonarjs/prefer-while': [2],
+ 'sort-imports': [0],
+ 'sort-keys': [0],
+ 'sort-vars': [0],
+ 'strict': [0],
+ 'symbol-description': [2],
+ 'unicode-bom': [2, 'never'],
+ 'unicorn/better-regex': [0],
+ 'unicorn/catch-error-name': [0],
+ 'unicorn/consistent-destructuring': [2],
+ 'unicorn/consistent-empty-array-spread': [2],
+ 'unicorn/consistent-existence-index-check': [0],
+ 'unicorn/consistent-function-scoping': [0],
+ 'unicorn/custom-error-definition': [0],
+ 'unicorn/empty-brace-spaces': [2],
+ 'unicorn/error-message': [0],
+ 'unicorn/escape-case': [0],
+ 'unicorn/expiring-todo-comments': [0],
+ 'unicorn/explicit-length-check': [0],
+ 'unicorn/filename-case': [0],
+ 'unicorn/import-index': [0],
+ 'unicorn/import-style': [0],
+ 'unicorn/new-for-builtins': [2],
+ 'unicorn/no-abusive-eslint-disable': [0],
+ 'unicorn/no-anonymous-default-export': [0],
+ 'unicorn/no-array-callback-reference': [0],
+ 'unicorn/no-array-for-each': [2],
+ 'unicorn/no-array-method-this-argument': [2],
+ 'unicorn/no-array-push-push': [2],
+ 'unicorn/no-array-reduce': [2],
+ 'unicorn/no-await-expression-member': [0],
+ 'unicorn/no-await-in-promise-methods': [2],
+ 'unicorn/no-console-spaces': [0],
+ 'unicorn/no-document-cookie': [2],
+ 'unicorn/no-empty-file': [2],
+ 'unicorn/no-for-loop': [0],
+ 'unicorn/no-hex-escape': [0],
+ 'unicorn/no-instanceof-array': [0],
+ 'unicorn/no-invalid-fetch-options': [2],
+ 'unicorn/no-invalid-remove-event-listener': [2],
+ 'unicorn/no-keyword-prefix': [0],
+ 'unicorn/no-length-as-slice-end': [2],
+ 'unicorn/no-lonely-if': [2],
+ 'unicorn/no-magic-array-flat-depth': [0],
+ 'unicorn/no-negated-condition': [0],
+ 'unicorn/no-negation-in-equality-check': [2],
+ 'unicorn/no-nested-ternary': [0],
+ 'unicorn/no-new-array': [0],
+ 'unicorn/no-new-buffer': [0],
+ 'unicorn/no-null': [0],
+ 'unicorn/no-object-as-default-parameter': [0],
+ 'unicorn/no-process-exit': [0],
+ 'unicorn/no-single-promise-in-promise-methods': [2],
+ 'unicorn/no-static-only-class': [2],
+ 'unicorn/no-thenable': [2],
+ 'unicorn/no-this-assignment': [2],
+ 'unicorn/no-typeof-undefined': [2],
+ 'unicorn/no-unnecessary-await': [2],
+ 'unicorn/no-unnecessary-polyfills': [2],
+ 'unicorn/no-unreadable-array-destructuring': [0],
+ 'unicorn/no-unreadable-iife': [2],
+ 'unicorn/no-unused-properties': [2],
+ 'unicorn/no-useless-fallback-in-spread': [2],
+ 'unicorn/no-useless-length-check': [2],
+ 'unicorn/no-useless-promise-resolve-reject': [2],
+ 'unicorn/no-useless-spread': [2],
+ 'unicorn/no-useless-switch-case': [2],
+ 'unicorn/no-useless-undefined': [0],
+ 'unicorn/no-zero-fractions': [2],
+ 'unicorn/number-literal-case': [0],
+ 'unicorn/numeric-separators-style': [0],
+ 'unicorn/prefer-add-event-listener': [2],
+ 'unicorn/prefer-array-find': [2],
+ 'unicorn/prefer-array-flat-map': [2],
+ 'unicorn/prefer-array-flat': [2],
+ 'unicorn/prefer-array-index-of': [2],
+ 'unicorn/prefer-array-some': [2],
+ 'unicorn/prefer-at': [0],
+ 'unicorn/prefer-blob-reading-methods': [2],
+ 'unicorn/prefer-code-point': [0],
+ 'unicorn/prefer-date-now': [2],
+ 'unicorn/prefer-default-parameters': [0],
+ 'unicorn/prefer-dom-node-append': [2],
+ 'unicorn/prefer-dom-node-dataset': [0],
+ 'unicorn/prefer-dom-node-remove': [2],
+ 'unicorn/prefer-dom-node-text-content': [2],
+ 'unicorn/prefer-event-target': [2],
+ 'unicorn/prefer-export-from': [0],
+ 'unicorn/prefer-global-this': [0],
+ 'unicorn/prefer-includes': [2],
+ 'unicorn/prefer-json-parse-buffer': [0],
+ 'unicorn/prefer-keyboard-event-key': [2],
+ 'unicorn/prefer-logical-operator-over-ternary': [2],
+ 'unicorn/prefer-math-min-max': [2],
+ 'unicorn/prefer-math-trunc': [2],
+ 'unicorn/prefer-modern-dom-apis': [0],
+ 'unicorn/prefer-modern-math-apis': [2],
+ 'unicorn/prefer-module': [2],
+ 'unicorn/prefer-native-coercion-functions': [2],
+ 'unicorn/prefer-negative-index': [2],
+ 'unicorn/prefer-node-protocol': [2],
+ 'unicorn/prefer-number-properties': [0],
+ 'unicorn/prefer-object-from-entries': [2],
+ 'unicorn/prefer-object-has-own': [0],
+ 'unicorn/prefer-optional-catch-binding': [2],
+ 'unicorn/prefer-prototype-methods': [0],
+ 'unicorn/prefer-query-selector': [2],
+ 'unicorn/prefer-reflect-apply': [0],
+ 'unicorn/prefer-regexp-test': [2],
+ 'unicorn/prefer-set-has': [0],
+ 'unicorn/prefer-set-size': [2],
+ 'unicorn/prefer-spread': [0],
+ 'unicorn/prefer-string-raw': [0],
+ 'unicorn/prefer-string-replace-all': [0],
+ 'unicorn/prefer-string-slice': [0],
+ 'unicorn/prefer-string-starts-ends-with': [2],
+ 'unicorn/prefer-string-trim-start-end': [2],
+ 'unicorn/prefer-structured-clone': [2],
+ 'unicorn/prefer-switch': [0],
+ 'unicorn/prefer-ternary': [0],
+ 'unicorn/prefer-text-content': [2],
+ 'unicorn/prefer-top-level-await': [0],
+ 'unicorn/prefer-type-error': [0],
+ 'unicorn/prevent-abbreviations': [0],
+ 'unicorn/relative-url-style': [2],
+ 'unicorn/require-array-join-separator': [2],
+ 'unicorn/require-number-to-fixed-digits-argument': [2],
+ 'unicorn/require-post-message-target-origin': [0],
+ 'unicorn/string-content': [0],
+ 'unicorn/switch-case-braces': [0],
+ 'unicorn/template-indent': [2],
+ 'unicorn/text-encoding-identifier-case': [0],
+ 'unicorn/throw-new-error': [2],
+ 'use-isnan': [2],
+ 'valid-typeof': [2, {requireStringLiterals: true}],
+ 'vars-on-top': [0],
+ 'wc/attach-shadow-constructor': [2],
+ 'wc/define-tag-after-class-definition': [0],
+ 'wc/expose-class-on-global': [0],
+ 'wc/file-name-matches-element': [2],
+ 'wc/guard-define-call': [0],
+ 'wc/guard-super-call': [2],
+ 'wc/max-elements-per-file': [0],
+ 'wc/no-child-traversal-in-attributechangedcallback': [2],
+ 'wc/no-child-traversal-in-connectedcallback': [2],
+ 'wc/no-closed-shadow-root': [2],
+ 'wc/no-constructor-attributes': [2],
+ 'wc/no-constructor-params': [2],
+ 'wc/no-constructor': [2],
+ 'wc/no-customized-built-in-elements': [2],
+ 'wc/no-exports-with-element': [0],
+ 'wc/no-invalid-element-name': [2],
+ 'wc/no-invalid-extends': [2],
+ 'wc/no-method-prefixed-with-on': [2],
+ 'wc/no-self-class': [2],
+ 'wc/no-typos': [2],
+ 'wc/require-listener-teardown': [2],
+ 'wc/tag-name-matches-class': [2],
+ 'yoda': [2, 'never'],
+ },
+};
diff --git a/.eslintrc.yaml b/.eslintrc.yaml
deleted file mode 100644
index 0dd9a6687d..0000000000
--- a/.eslintrc.yaml
+++ /dev/null
@@ -1,967 +0,0 @@
-root: true
-reportUnusedDisableDirectives: true
-
-ignorePatterns:
- - /web_src/js/vendor
- - /web_src/fomantic
- - /public/assets/js
-
-parser: "@typescript-eslint/parser"
-
-parserOptions:
- sourceType: module
- ecmaVersion: latest
- project: true
- extraFileExtensions: [".vue"]
- parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
-
-settings:
- import-x/extensions: [".js", ".ts"]
- import-x/parsers:
- "@typescript-eslint/parser": [".js", ".ts"]
- import-x/resolver:
- typescript: true
-
-plugins:
- - "@eslint-community/eslint-plugin-eslint-comments"
- - "@stylistic/eslint-plugin-js"
- - "@typescript-eslint/eslint-plugin"
- - eslint-plugin-array-func
- - eslint-plugin-github
- - eslint-plugin-import-x
- - eslint-plugin-no-jquery
- - eslint-plugin-no-use-extend-native
- - eslint-plugin-regexp
- - eslint-plugin-sonarjs
- - eslint-plugin-unicorn
- - eslint-plugin-vitest
- - eslint-plugin-vitest-globals
- - eslint-plugin-wc
-
-env:
- es2024: true
- node: true
-
-overrides:
- - files: ["web_src/**/*"]
- globals:
- __webpack_public_path__: true
- process: false # https://github.com/webpack/webpack/issues/15833
- - files: ["web_src/**/*", "docs/**/*"]
- env:
- browser: true
- node: false
- - files: ["web_src/**/*worker.*"]
- env:
- worker: true
- rules:
- no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
- - files: ["*.config.*"]
- rules:
- import-x/no-unused-modules: [0]
- - files: ["**/*.d.ts"]
- rules:
- import-x/no-unused-modules: [0]
- "@typescript-eslint/consistent-type-definitions": [0]
- "@typescript-eslint/consistent-type-imports": [0]
- - files: ["web_src/js/types.ts"]
- rules:
- import-x/no-unused-modules: [0]
- - files: ["**/*.test.*", "web_src/js/test/setup.ts"]
- env:
- vitest-globals/env: true
- rules:
- vitest/consistent-test-filename: [0]
- vitest/consistent-test-it: [0]
- vitest/expect-expect: [0]
- vitest/max-expects: [0]
- vitest/max-nested-describe: [0]
- vitest/no-alias-methods: [0]
- vitest/no-commented-out-tests: [0]
- vitest/no-conditional-expect: [0]
- vitest/no-conditional-in-test: [0]
- vitest/no-conditional-tests: [0]
- vitest/no-disabled-tests: [0]
- vitest/no-done-callback: [0]
- vitest/no-duplicate-hooks: [0]
- vitest/no-focused-tests: [0]
- vitest/no-hooks: [0]
- vitest/no-identical-title: [2]
- vitest/no-interpolation-in-snapshots: [0]
- vitest/no-large-snapshots: [0]
- vitest/no-mocks-import: [0]
- vitest/no-restricted-matchers: [0]
- vitest/no-restricted-vi-methods: [0]
- vitest/no-standalone-expect: [0]
- vitest/no-test-prefixes: [0]
- vitest/no-test-return-statement: [0]
- vitest/prefer-called-with: [0]
- vitest/prefer-comparison-matcher: [0]
- vitest/prefer-each: [0]
- vitest/prefer-equality-matcher: [0]
- vitest/prefer-expect-resolves: [0]
- vitest/prefer-hooks-in-order: [0]
- vitest/prefer-hooks-on-top: [2]
- vitest/prefer-lowercase-title: [0]
- vitest/prefer-mock-promise-shorthand: [0]
- vitest/prefer-snapshot-hint: [0]
- vitest/prefer-spy-on: [0]
- vitest/prefer-strict-equal: [0]
- vitest/prefer-to-be: [0]
- vitest/prefer-to-be-falsy: [0]
- vitest/prefer-to-be-object: [0]
- vitest/prefer-to-be-truthy: [0]
- vitest/prefer-to-contain: [0]
- vitest/prefer-to-have-length: [0]
- vitest/prefer-todo: [0]
- vitest/require-hook: [0]
- vitest/require-to-throw-message: [0]
- vitest/require-top-level-describe: [0]
- vitest/valid-describe-callback: [2]
- vitest/valid-expect: [2]
- vitest/valid-title: [2]
- - files: ["web_src/js/modules/fetch.ts", "web_src/js/standalone/**/*"]
- rules:
- no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
- - files: ["**/*.vue"]
- plugins:
- - eslint-plugin-vue
- - eslint-plugin-vue-scoped-css
- extends:
- - plugin:vue/vue3-recommended
- - plugin:vue-scoped-css/vue3-recommended
- rules:
- vue/attributes-order: [0]
- vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}]
- vue/max-attributes-per-line: [0]
- vue/singleline-html-element-content-newline: [0]
- - files: ["tests/e2e/**"]
- plugins:
- - eslint-plugin-playwright
- extends: plugin:playwright/recommended
-
-rules:
- "@eslint-community/eslint-comments/disable-enable-pair": [2]
- "@eslint-community/eslint-comments/no-aggregating-enable": [2]
- "@eslint-community/eslint-comments/no-duplicate-disable": [2]
- "@eslint-community/eslint-comments/no-restricted-disable": [0]
- "@eslint-community/eslint-comments/no-unlimited-disable": [2]
- "@eslint-community/eslint-comments/no-unused-disable": [2]
- "@eslint-community/eslint-comments/no-unused-enable": [2]
- "@eslint-community/eslint-comments/no-use": [0]
- "@eslint-community/eslint-comments/require-description": [0]
- "@stylistic/js/array-bracket-newline": [0]
- "@stylistic/js/array-bracket-spacing": [2, never]
- "@stylistic/js/array-element-newline": [0]
- "@stylistic/js/arrow-parens": [2, always]
- "@stylistic/js/arrow-spacing": [2, {before: true, after: true}]
- "@stylistic/js/block-spacing": [0]
- "@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}]
- "@stylistic/js/comma-dangle": [2, always-multiline]
- "@stylistic/js/comma-spacing": [2, {before: false, after: true}]
- "@stylistic/js/comma-style": [2, last]
- "@stylistic/js/computed-property-spacing": [2, never]
- "@stylistic/js/dot-location": [2, property]
- "@stylistic/js/eol-last": [2]
- "@stylistic/js/function-call-argument-newline": [0]
- "@stylistic/js/function-call-spacing": [2, never]
- "@stylistic/js/function-paren-newline": [0]
- "@stylistic/js/generator-star-spacing": [0]
- "@stylistic/js/implicit-arrow-linebreak": [0]
- "@stylistic/js/indent": [2, 2, {ignoreComments: true, SwitchCase: 1}]
- "@stylistic/js/key-spacing": [2]
- "@stylistic/js/keyword-spacing": [2]
- "@stylistic/js/line-comment-position": [0]
- "@stylistic/js/linebreak-style": [2, unix]
- "@stylistic/js/lines-around-comment": [0]
- "@stylistic/js/lines-between-class-members": [0]
- "@stylistic/js/max-len": [0]
- "@stylistic/js/max-statements-per-line": [0]
- "@stylistic/js/multiline-comment-style": [0]
- "@stylistic/js/multiline-ternary": [0]
- "@stylistic/js/new-parens": [2]
- "@stylistic/js/newline-per-chained-call": [0]
- "@stylistic/js/no-confusing-arrow": [0]
- "@stylistic/js/no-extra-parens": [0]
- "@stylistic/js/no-extra-semi": [2]
- "@stylistic/js/no-floating-decimal": [0]
- "@stylistic/js/no-mixed-operators": [0]
- "@stylistic/js/no-mixed-spaces-and-tabs": [2]
- "@stylistic/js/no-multi-spaces": [2, {ignoreEOLComments: true, exceptions: {Property: true}}]
- "@stylistic/js/no-multiple-empty-lines": [2, {max: 1, maxEOF: 0, maxBOF: 0}]
- "@stylistic/js/no-tabs": [2]
- "@stylistic/js/no-trailing-spaces": [2]
- "@stylistic/js/no-whitespace-before-property": [2]
- "@stylistic/js/nonblock-statement-body-position": [2]
- "@stylistic/js/object-curly-newline": [0]
- "@stylistic/js/object-curly-spacing": [2, never]
- "@stylistic/js/object-property-newline": [0]
- "@stylistic/js/one-var-declaration-per-line": [0]
- "@stylistic/js/operator-linebreak": [2, after]
- "@stylistic/js/padded-blocks": [2, never]
- "@stylistic/js/padding-line-between-statements": [0]
- "@stylistic/js/quote-props": [0]
- "@stylistic/js/quotes": [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
- "@stylistic/js/rest-spread-spacing": [2, never]
- "@stylistic/js/semi": [2, always, {omitLastInOneLineBlock: true}]
- "@stylistic/js/semi-spacing": [2, {before: false, after: true}]
- "@stylistic/js/semi-style": [2, last]
- "@stylistic/js/space-before-blocks": [2, always]
- "@stylistic/js/space-before-function-paren": [2, {anonymous: ignore, named: never, asyncArrow: always}]
- "@stylistic/js/space-in-parens": [2, never]
- "@stylistic/js/space-infix-ops": [2]
- "@stylistic/js/space-unary-ops": [2]
- "@stylistic/js/spaced-comment": [2, always]
- "@stylistic/js/switch-colon-spacing": [2]
- "@stylistic/js/template-curly-spacing": [2, never]
- "@stylistic/js/template-tag-spacing": [2, never]
- "@stylistic/js/wrap-iife": [2, inside]
- "@stylistic/js/wrap-regex": [0]
- "@stylistic/js/yield-star-spacing": [2, after]
- "@typescript-eslint/adjacent-overload-signatures": [0]
- "@typescript-eslint/array-type": [0]
- "@typescript-eslint/await-thenable": [2]
- "@typescript-eslint/ban-ts-comment": [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}]
- "@typescript-eslint/ban-tslint-comment": [0]
- "@typescript-eslint/class-literal-property-style": [0]
- "@typescript-eslint/class-methods-use-this": [0]
- "@typescript-eslint/consistent-generic-constructors": [0]
- "@typescript-eslint/consistent-indexed-object-style": [0]
- "@typescript-eslint/consistent-return": [0]
- "@typescript-eslint/consistent-type-assertions": [2, {assertionStyle: as, objectLiteralTypeAssertions: allow}]
- "@typescript-eslint/consistent-type-definitions": [2, type]
- "@typescript-eslint/consistent-type-exports": [2, {fixMixedExportsWithInlineTypeSpecifier: false}]
- "@typescript-eslint/consistent-type-imports": [2, {prefer: type-imports, fixStyle: separate-type-imports, disallowTypeAnnotations: true}]
- "@typescript-eslint/default-param-last": [0]
- "@typescript-eslint/dot-notation": [0]
- "@typescript-eslint/explicit-function-return-type": [0]
- "@typescript-eslint/explicit-member-accessibility": [0]
- "@typescript-eslint/explicit-module-boundary-types": [0]
- "@typescript-eslint/init-declarations": [0]
- "@typescript-eslint/max-params": [0]
- "@typescript-eslint/member-ordering": [0]
- "@typescript-eslint/method-signature-style": [0]
- "@typescript-eslint/naming-convention": [0]
- "@typescript-eslint/no-array-constructor": [2]
- "@typescript-eslint/no-array-delete": [2]
- "@typescript-eslint/no-base-to-string": [0]
- "@typescript-eslint/no-confusing-non-null-assertion": [2]
- "@typescript-eslint/no-confusing-void-expression": [0]
- "@typescript-eslint/no-deprecated": [2]
- "@typescript-eslint/no-dupe-class-members": [0]
- "@typescript-eslint/no-duplicate-enum-values": [2]
- "@typescript-eslint/no-duplicate-type-constituents": [2, {ignoreUnions: true}]
- "@typescript-eslint/no-dynamic-delete": [0]
- "@typescript-eslint/no-empty-function": [0]
- "@typescript-eslint/no-empty-interface": [0]
- "@typescript-eslint/no-empty-object-type": [2]
- "@typescript-eslint/no-explicit-any": [0]
- "@typescript-eslint/no-extra-non-null-assertion": [2]
- "@typescript-eslint/no-extraneous-class": [0]
- "@typescript-eslint/no-floating-promises": [0]
- "@typescript-eslint/no-for-in-array": [2]
- "@typescript-eslint/no-implied-eval": [2]
- "@typescript-eslint/no-import-type-side-effects": [0] # dupe with consistent-type-imports
- "@typescript-eslint/no-inferrable-types": [0]
- "@typescript-eslint/no-invalid-this": [0]
- "@typescript-eslint/no-invalid-void-type": [0]
- "@typescript-eslint/no-loop-func": [0]
- "@typescript-eslint/no-loss-of-precision": [0]
- "@typescript-eslint/no-magic-numbers": [0]
- "@typescript-eslint/no-meaningless-void-operator": [0]
- "@typescript-eslint/no-misused-new": [2]
- "@typescript-eslint/no-misused-promises": [2, {checksVoidReturn: {attributes: false, arguments: false}}]
- "@typescript-eslint/no-mixed-enums": [0]
- "@typescript-eslint/no-namespace": [2]
- "@typescript-eslint/no-non-null-asserted-nullish-coalescing": [0]
- "@typescript-eslint/no-non-null-asserted-optional-chain": [2]
- "@typescript-eslint/no-non-null-assertion": [0]
- "@typescript-eslint/no-redeclare": [0]
- "@typescript-eslint/no-redundant-type-constituents": [2]
- "@typescript-eslint/no-require-imports": [2]
- "@typescript-eslint/no-restricted-imports": [0]
- "@typescript-eslint/no-restricted-types": [0]
- "@typescript-eslint/no-shadow": [0]
- "@typescript-eslint/no-this-alias": [0] # handled by unicorn/no-this-assignment
- "@typescript-eslint/no-unnecessary-boolean-literal-compare": [0]
- "@typescript-eslint/no-unnecessary-condition": [0]
- "@typescript-eslint/no-unnecessary-qualifier": [0]
- "@typescript-eslint/no-unnecessary-template-expression": [0]
- "@typescript-eslint/no-unnecessary-type-arguments": [0]
- "@typescript-eslint/no-unnecessary-type-assertion": [2]
- "@typescript-eslint/no-unnecessary-type-constraint": [2]
- "@typescript-eslint/no-unsafe-argument": [0]
- "@typescript-eslint/no-unsafe-assignment": [0]
- "@typescript-eslint/no-unsafe-call": [0]
- "@typescript-eslint/no-unsafe-declaration-merging": [2]
- "@typescript-eslint/no-unsafe-enum-comparison": [2]
- "@typescript-eslint/no-unsafe-function-type": [2]
- "@typescript-eslint/no-unsafe-member-access": [0]
- "@typescript-eslint/no-unsafe-return": [0]
- "@typescript-eslint/no-unsafe-unary-minus": [2]
- "@typescript-eslint/no-unused-expressions": [0]
- "@typescript-eslint/no-unused-vars": [2, {vars: all, args: all, caughtErrors: all, ignoreRestSiblings: false, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_}]
- "@typescript-eslint/no-use-before-define": [0]
- "@typescript-eslint/no-useless-constructor": [0]
- "@typescript-eslint/no-useless-empty-export": [0]
- "@typescript-eslint/no-wrapper-object-types": [2]
- "@typescript-eslint/non-nullable-type-assertion-style": [0]
- "@typescript-eslint/only-throw-error": [2]
- "@typescript-eslint/parameter-properties": [0]
- "@typescript-eslint/prefer-as-const": [2]
- "@typescript-eslint/prefer-destructuring": [0]
- "@typescript-eslint/prefer-enum-initializers": [0]
- "@typescript-eslint/prefer-find": [2]
- "@typescript-eslint/prefer-for-of": [2]
- "@typescript-eslint/prefer-function-type": [2]
- "@typescript-eslint/prefer-includes": [2]
- "@typescript-eslint/prefer-literal-enum-member": [0]
- "@typescript-eslint/prefer-namespace-keyword": [0]
- "@typescript-eslint/prefer-nullish-coalescing": [0]
- "@typescript-eslint/prefer-optional-chain": [2, {requireNullish: true}]
- "@typescript-eslint/prefer-promise-reject-errors": [0]
- "@typescript-eslint/prefer-readonly": [0]
- "@typescript-eslint/prefer-readonly-parameter-types": [0]
- "@typescript-eslint/prefer-reduce-type-parameter": [0]
- "@typescript-eslint/prefer-regexp-exec": [0]
- "@typescript-eslint/prefer-return-this-type": [0]
- "@typescript-eslint/prefer-string-starts-ends-with": [2, {allowSingleElementEquality: always}]
- "@typescript-eslint/promise-function-async": [0]
- "@typescript-eslint/require-array-sort-compare": [0]
- "@typescript-eslint/require-await": [0]
- "@typescript-eslint/restrict-plus-operands": [2]
- "@typescript-eslint/restrict-template-expressions": [0]
- "@typescript-eslint/return-await": [0]
- "@typescript-eslint/strict-boolean-expressions": [0]
- "@typescript-eslint/switch-exhaustiveness-check": [0]
- "@typescript-eslint/triple-slash-reference": [2]
- "@typescript-eslint/typedef": [0]
- "@typescript-eslint/unbound-method": [0] # too many false-positives
- "@typescript-eslint/unified-signatures": [2]
- accessor-pairs: [2]
- array-callback-return: [2, {checkForEach: true}]
- array-func/avoid-reverse: [2]
- array-func/from-map: [2]
- array-func/no-unnecessary-this-arg: [2]
- array-func/prefer-array-from: [2]
- array-func/prefer-flat-map: [0] # handled by unicorn/prefer-array-flat-map
- array-func/prefer-flat: [0] # handled by unicorn/prefer-array-flat
- arrow-body-style: [0]
- block-scoped-var: [2]
- camelcase: [0]
- capitalized-comments: [0]
- class-methods-use-this: [0]
- complexity: [0]
- consistent-return: [0]
- consistent-this: [0]
- constructor-super: [2]
- curly: [0]
- default-case-last: [2]
- default-case: [0]
- default-param-last: [0]
- dot-notation: [0]
- eqeqeq: [2]
- for-direction: [2]
- func-name-matching: [2]
- func-names: [0]
- func-style: [0]
- getter-return: [2]
- github/a11y-aria-label-is-well-formatted: [0]
- github/a11y-no-title-attribute: [0]
- github/a11y-no-visually-hidden-interactive-element: [0]
- github/a11y-role-supports-aria-props: [0]
- github/a11y-svg-has-accessible-name: [0]
- github/array-foreach: [0]
- github/async-currenttarget: [2]
- github/async-preventdefault: [2]
- github/authenticity-token: [0]
- github/get-attribute: [0]
- github/js-class-name: [0]
- github/no-blur: [0]
- github/no-d-none: [0]
- github/no-dataset: [2]
- github/no-dynamic-script-tag: [2]
- github/no-implicit-buggy-globals: [2]
- github/no-inner-html: [0]
- github/no-innerText: [2]
- github/no-then: [2]
- github/no-useless-passive: [2]
- github/prefer-observers: [2]
- github/require-passive-events: [2]
- github/unescaped-html-literal: [0]
- grouped-accessor-pairs: [2]
- guard-for-in: [0]
- id-blacklist: [0]
- id-length: [0]
- id-match: [0]
- import-x/consistent-type-specifier-style: [0]
- import-x/default: [0]
- import-x/dynamic-import-chunkname: [0]
- import-x/export: [2]
- import-x/exports-last: [0]
- import-x/extensions: [2, always, {ignorePackages: true}]
- import-x/first: [2]
- import-x/group-exports: [0]
- import-x/max-dependencies: [0]
- import-x/named: [2]
- import-x/namespace: [0]
- import-x/newline-after-import: [0]
- import-x/no-absolute-path: [0]
- import-x/no-amd: [2]
- import-x/no-anonymous-default-export: [0]
- import-x/no-commonjs: [2]
- import-x/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
- import-x/no-default-export: [0]
- import-x/no-deprecated: [0]
- import-x/no-dynamic-require: [0]
- import-x/no-empty-named-blocks: [2]
- import-x/no-extraneous-dependencies: [2]
- import-x/no-import-module-exports: [0]
- import-x/no-internal-modules: [0]
- import-x/no-mutable-exports: [0]
- import-x/no-named-as-default-member: [0]
- import-x/no-named-as-default: [0]
- import-x/no-named-default: [0]
- import-x/no-named-export: [0]
- import-x/no-namespace: [0]
- import-x/no-nodejs-modules: [0]
- import-x/no-relative-packages: [0]
- import-x/no-relative-parent-imports: [0]
- import-x/no-restricted-paths: [0]
- import-x/no-self-import: [2]
- import-x/no-unassigned-import: [0]
- import-x/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
- import-x/no-unused-modules: [2, {unusedExports: true}]
- import-x/no-useless-path-segments: [2, {commonjs: true}]
- import-x/no-webpack-loader-syntax: [2]
- import-x/order: [0]
- import-x/prefer-default-export: [0]
- import-x/unambiguous: [0]
- init-declarations: [0]
- line-comment-position: [0]
- logical-assignment-operators: [0]
- max-classes-per-file: [0]
- max-depth: [0]
- max-lines-per-function: [0]
- max-lines: [0]
- max-nested-callbacks: [0]
- max-params: [0]
- max-statements: [0]
- multiline-comment-style: [2, separate-lines]
- new-cap: [0]
- no-alert: [0]
- no-array-constructor: [0] # handled by @typescript-eslint/no-array-constructor
- no-async-promise-executor: [0]
- no-await-in-loop: [0]
- no-bitwise: [0]
- no-buffer-constructor: [0]
- no-caller: [2]
- no-case-declarations: [2]
- no-class-assign: [2]
- no-compare-neg-zero: [2]
- no-cond-assign: [2, except-parens]
- no-console: [1, {allow: [debug, info, warn, error]}]
- no-const-assign: [2]
- no-constant-binary-expression: [2]
- no-constant-condition: [0]
- no-constructor-return: [2]
- no-continue: [0]
- no-control-regex: [0]
- no-debugger: [1]
- no-delete-var: [2]
- no-div-regex: [0]
- no-dupe-args: [2]
- no-dupe-class-members: [2]
- no-dupe-else-if: [2]
- no-dupe-keys: [2]
- no-duplicate-case: [2]
- no-duplicate-imports: [0]
- no-else-return: [2]
- no-empty-character-class: [2]
- no-empty-function: [0]
- no-empty-pattern: [2]
- no-empty-static-block: [2]
- no-empty: [2, {allowEmptyCatch: true}]
- no-eq-null: [2]
- no-eval: [2]
- no-ex-assign: [2]
- no-extend-native: [2]
- no-extra-bind: [2]
- no-extra-boolean-cast: [2]
- no-extra-label: [0]
- no-fallthrough: [2]
- no-func-assign: [2]
- no-global-assign: [2]
- no-implicit-coercion: [2]
- no-implicit-globals: [0]
- no-implied-eval: [0] # handled by @typescript-eslint/no-implied-eval
- no-import-assign: [2]
- no-inline-comments: [0]
- no-inner-declarations: [2]
- no-invalid-regexp: [2]
- no-invalid-this: [0]
- no-irregular-whitespace: [2]
- no-iterator: [2]
- no-jquery/no-ajax-events: [2]
- no-jquery/no-ajax: [2]
- no-jquery/no-and-self: [2]
- no-jquery/no-animate-toggle: [2]
- no-jquery/no-animate: [2]
- no-jquery/no-append-html: [2]
- no-jquery/no-attr: [2]
- no-jquery/no-bind: [2]
- no-jquery/no-box-model: [2]
- no-jquery/no-browser: [2]
- no-jquery/no-camel-case: [2]
- no-jquery/no-class-state: [2]
- no-jquery/no-class: [0]
- no-jquery/no-clone: [2]
- no-jquery/no-closest: [0]
- no-jquery/no-constructor-attributes: [2]
- no-jquery/no-contains: [2]
- no-jquery/no-context-prop: [2]
- no-jquery/no-css: [2]
- no-jquery/no-data: [0]
- no-jquery/no-deferred: [2]
- no-jquery/no-delegate: [2]
- no-jquery/no-done-fail: [2]
- no-jquery/no-each-collection: [0]
- no-jquery/no-each-util: [0]
- no-jquery/no-each: [0]
- no-jquery/no-error-shorthand: [2]
- no-jquery/no-error: [2]
- no-jquery/no-escape-selector: [2]
- no-jquery/no-event-shorthand: [2]
- no-jquery/no-extend: [2]
- no-jquery/no-fade: [2]
- no-jquery/no-filter: [0]
- no-jquery/no-find-collection: [0]
- no-jquery/no-find-util: [2]
- no-jquery/no-find: [0]
- no-jquery/no-fx-interval: [2]
- no-jquery/no-fx: [2]
- no-jquery/no-global-eval: [2]
- no-jquery/no-global-selector: [0]
- no-jquery/no-grep: [2]
- no-jquery/no-has: [2]
- no-jquery/no-hold-ready: [2]
- no-jquery/no-html: [0]
- no-jquery/no-in-array: [2]
- no-jquery/no-is-array: [2]
- no-jquery/no-is-empty-object: [2]
- no-jquery/no-is-function: [2]
- no-jquery/no-is-numeric: [2]
- no-jquery/no-is-plain-object: [2]
- no-jquery/no-is-window: [2]
- no-jquery/no-is: [2]
- no-jquery/no-jquery-constructor: [0]
- no-jquery/no-live: [2]
- no-jquery/no-load-shorthand: [2]
- no-jquery/no-load: [2]
- no-jquery/no-map-collection: [0]
- no-jquery/no-map-util: [2]
- no-jquery/no-map: [2]
- no-jquery/no-merge: [2]
- no-jquery/no-node-name: [2]
- no-jquery/no-noop: [2]
- no-jquery/no-now: [2]
- no-jquery/no-on-ready: [2]
- no-jquery/no-other-methods: [0]
- no-jquery/no-other-utils: [2]
- no-jquery/no-param: [2]
- no-jquery/no-parent: [0]
- no-jquery/no-parents: [2]
- no-jquery/no-parse-html-literal: [2]
- no-jquery/no-parse-html: [2]
- no-jquery/no-parse-json: [2]
- no-jquery/no-parse-xml: [2]
- no-jquery/no-prop: [2]
- no-jquery/no-proxy: [2]
- no-jquery/no-ready-shorthand: [2]
- no-jquery/no-ready: [2]
- no-jquery/no-selector-prop: [2]
- no-jquery/no-serialize: [2]
- no-jquery/no-size: [2]
- no-jquery/no-sizzle: [2]
- no-jquery/no-slide: [2]
- no-jquery/no-sub: [2]
- no-jquery/no-support: [2]
- no-jquery/no-text: [2]
- no-jquery/no-trigger: [0]
- no-jquery/no-trim: [2]
- no-jquery/no-type: [2]
- no-jquery/no-unique: [2]
- no-jquery/no-unload-shorthand: [2]
- no-jquery/no-val: [0]
- no-jquery/no-visibility: [2]
- no-jquery/no-when: [2]
- no-jquery/no-wrap: [2]
- no-jquery/variable-pattern: [2]
- no-label-var: [2]
- no-labels: [0] # handled by no-restricted-syntax
- no-lone-blocks: [2]
- no-lonely-if: [0]
- no-loop-func: [0]
- no-loss-of-precision: [2]
- no-magic-numbers: [0]
- no-misleading-character-class: [2]
- no-multi-assign: [0]
- no-multi-str: [2]
- no-negated-condition: [0]
- no-nested-ternary: [0]
- no-new-func: [2]
- no-new-native-nonconstructor: [2]
- no-new-object: [2]
- no-new-symbol: [2]
- no-new-wrappers: [2]
- no-new: [0]
- no-nonoctal-decimal-escape: [2]
- no-obj-calls: [2]
- no-octal-escape: [2]
- no-octal: [2]
- no-param-reassign: [0]
- no-plusplus: [0]
- no-promise-executor-return: [0]
- no-proto: [2]
- no-prototype-builtins: [2]
- no-redeclare: [0] # must be disabled for typescript overloads
- no-regex-spaces: [2]
- no-restricted-exports: [0]
- no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
- no-restricted-imports: [0]
- no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.ts instead"}]
- no-return-assign: [0]
- no-script-url: [2]
- no-self-assign: [2, {props: true}]
- no-self-compare: [2]
- no-sequences: [2]
- no-setter-return: [2]
- no-shadow-restricted-names: [2]
- no-shadow: [0]
- no-sparse-arrays: [2]
- no-template-curly-in-string: [2]
- no-ternary: [0]
- no-this-before-super: [2]
- no-throw-literal: [2]
- no-undef-init: [2]
- no-undef: [2, {typeof: true}] # TODO: disable this rule after tsc passes
- no-undefined: [0]
- no-underscore-dangle: [0]
- no-unexpected-multiline: [2]
- no-unmodified-loop-condition: [2]
- no-unneeded-ternary: [2]
- no-unreachable-loop: [2]
- no-unreachable: [2]
- no-unsafe-finally: [2]
- no-unsafe-negation: [2]
- no-unused-expressions: [2]
- no-unused-labels: [2]
- no-unused-private-class-members: [2]
- no-unused-vars: [0] # handled by @typescript-eslint/no-unused-vars
- no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}]
- no-use-extend-native/no-use-extend-native: [2]
- no-useless-backreference: [2]
- no-useless-call: [2]
- no-useless-catch: [2]
- no-useless-computed-key: [2]
- no-useless-concat: [2]
- no-useless-constructor: [2]
- no-useless-escape: [2]
- no-useless-rename: [2]
- no-useless-return: [2]
- no-var: [2]
- no-void: [2]
- no-warning-comments: [0]
- no-with: [0] # handled by no-restricted-syntax
- object-shorthand: [2, always]
- one-var-declaration-per-line: [0]
- one-var: [0]
- operator-assignment: [2, always]
- operator-linebreak: [2, after]
- prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
- prefer-const: [2, {destructuring: all, ignoreReadBeforeAssign: true}]
- prefer-destructuring: [0]
- prefer-exponentiation-operator: [2]
- prefer-named-capture-group: [0]
- prefer-numeric-literals: [2]
- prefer-object-has-own: [2]
- prefer-object-spread: [2]
- prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
- prefer-regex-literals: [2]
- prefer-rest-params: [2]
- prefer-spread: [2]
- prefer-template: [2]
- radix: [2, as-needed]
- regexp/confusing-quantifier: [2]
- regexp/control-character-escape: [2]
- regexp/hexadecimal-escape: [0]
- regexp/letter-case: [0]
- regexp/match-any: [2]
- regexp/negation: [2]
- regexp/no-contradiction-with-assertion: [0]
- regexp/no-control-character: [0]
- regexp/no-dupe-characters-character-class: [2]
- regexp/no-dupe-disjunctions: [2]
- regexp/no-empty-alternative: [2]
- regexp/no-empty-capturing-group: [2]
- regexp/no-empty-character-class: [0]
- regexp/no-empty-group: [2]
- regexp/no-empty-lookarounds-assertion: [2]
- regexp/no-empty-string-literal: [2]
- regexp/no-escape-backspace: [2]
- regexp/no-extra-lookaround-assertions: [0]
- regexp/no-invalid-regexp: [2]
- regexp/no-invisible-character: [2]
- regexp/no-lazy-ends: [2]
- regexp/no-legacy-features: [2]
- regexp/no-misleading-capturing-group: [0]
- regexp/no-misleading-unicode-character: [0]
- regexp/no-missing-g-flag: [2]
- regexp/no-non-standard-flag: [2]
- regexp/no-obscure-range: [2]
- regexp/no-octal: [2]
- regexp/no-optional-assertion: [2]
- regexp/no-potentially-useless-backreference: [2]
- regexp/no-standalone-backslash: [2]
- regexp/no-super-linear-backtracking: [0]
- regexp/no-super-linear-move: [0]
- regexp/no-trivially-nested-assertion: [2]
- regexp/no-trivially-nested-quantifier: [2]
- regexp/no-unused-capturing-group: [0]
- regexp/no-useless-assertions: [2]
- regexp/no-useless-backreference: [2]
- regexp/no-useless-character-class: [2]
- regexp/no-useless-dollar-replacements: [2]
- regexp/no-useless-escape: [2]
- regexp/no-useless-flag: [2]
- regexp/no-useless-lazy: [2]
- regexp/no-useless-non-capturing-group: [2]
- regexp/no-useless-quantifier: [2]
- regexp/no-useless-range: [2]
- regexp/no-useless-set-operand: [2]
- regexp/no-useless-string-literal: [2]
- regexp/no-useless-two-nums-quantifier: [2]
- regexp/no-zero-quantifier: [2]
- regexp/optimal-lookaround-quantifier: [2]
- regexp/optimal-quantifier-concatenation: [0]
- regexp/prefer-character-class: [0]
- regexp/prefer-d: [0]
- regexp/prefer-escape-replacement-dollar-char: [0]
- regexp/prefer-lookaround: [0]
- regexp/prefer-named-backreference: [0]
- regexp/prefer-named-capture-group: [0]
- regexp/prefer-named-replacement: [0]
- regexp/prefer-plus-quantifier: [2]
- regexp/prefer-predefined-assertion: [2]
- regexp/prefer-quantifier: [0]
- regexp/prefer-question-quantifier: [2]
- regexp/prefer-range: [2]
- regexp/prefer-regexp-exec: [2]
- regexp/prefer-regexp-test: [2]
- regexp/prefer-result-array-groups: [0]
- regexp/prefer-set-operation: [2]
- regexp/prefer-star-quantifier: [2]
- regexp/prefer-unicode-codepoint-escapes: [2]
- regexp/prefer-w: [0]
- regexp/require-unicode-regexp: [0]
- regexp/simplify-set-operations: [2]
- regexp/sort-alternatives: [0]
- regexp/sort-character-class-elements: [0]
- regexp/sort-flags: [0]
- regexp/strict: [2]
- regexp/unicode-escape: [0]
- regexp/use-ignore-case: [0]
- require-atomic-updates: [0]
- require-await: [0] # handled by @typescript-eslint/require-await
- require-unicode-regexp: [0]
- require-yield: [2]
- sonarjs/cognitive-complexity: [0]
- sonarjs/elseif-without-else: [0]
- sonarjs/max-switch-cases: [0]
- sonarjs/no-all-duplicated-branches: [2]
- sonarjs/no-collapsible-if: [0]
- sonarjs/no-collection-size-mischeck: [2]
- sonarjs/no-duplicate-string: [0]
- sonarjs/no-duplicated-branches: [0]
- sonarjs/no-element-overwrite: [2]
- sonarjs/no-empty-collection: [2]
- sonarjs/no-extra-arguments: [2]
- sonarjs/no-gratuitous-expressions: [2]
- sonarjs/no-identical-conditions: [2]
- sonarjs/no-identical-expressions: [2]
- sonarjs/no-identical-functions: [2, 5]
- sonarjs/no-ignored-return: [2]
- sonarjs/no-inverted-boolean-check: [2]
- sonarjs/no-nested-switch: [0]
- sonarjs/no-nested-template-literals: [0]
- sonarjs/no-one-iteration-loop: [2]
- sonarjs/no-redundant-boolean: [2]
- sonarjs/no-redundant-jump: [2]
- sonarjs/no-same-line-conditional: [2]
- sonarjs/no-small-switch: [0]
- sonarjs/no-unused-collection: [2]
- sonarjs/no-use-of-empty-return-value: [2]
- sonarjs/no-useless-catch: [2]
- sonarjs/non-existent-operator: [2]
- sonarjs/prefer-immediate-return: [0]
- sonarjs/prefer-object-literal: [0]
- sonarjs/prefer-single-boolean-return: [0]
- sonarjs/prefer-while: [2]
- sort-imports: [0]
- sort-keys: [0]
- sort-vars: [0]
- strict: [0]
- symbol-description: [2]
- unicode-bom: [2, never]
- unicorn/better-regex: [0]
- unicorn/catch-error-name: [0]
- unicorn/consistent-destructuring: [2]
- unicorn/consistent-empty-array-spread: [2]
- unicorn/consistent-existence-index-check: [0]
- unicorn/consistent-function-scoping: [0]
- unicorn/custom-error-definition: [0]
- unicorn/empty-brace-spaces: [2]
- unicorn/error-message: [0]
- unicorn/escape-case: [0]
- unicorn/expiring-todo-comments: [0]
- unicorn/explicit-length-check: [0]
- unicorn/filename-case: [0]
- unicorn/import-index: [0]
- unicorn/import-style: [0]
- unicorn/new-for-builtins: [2]
- unicorn/no-abusive-eslint-disable: [0]
- unicorn/no-anonymous-default-export: [0]
- unicorn/no-array-callback-reference: [0]
- unicorn/no-array-for-each: [2]
- unicorn/no-array-method-this-argument: [2]
- unicorn/no-array-push-push: [2]
- unicorn/no-array-reduce: [2]
- unicorn/no-await-expression-member: [0]
- unicorn/no-await-in-promise-methods: [2]
- unicorn/no-console-spaces: [0]
- unicorn/no-document-cookie: [2]
- unicorn/no-empty-file: [2]
- unicorn/no-for-loop: [0]
- unicorn/no-hex-escape: [0]
- unicorn/no-instanceof-array: [0]
- unicorn/no-invalid-fetch-options: [2]
- unicorn/no-invalid-remove-event-listener: [2]
- unicorn/no-keyword-prefix: [0]
- unicorn/no-length-as-slice-end: [2]
- unicorn/no-lonely-if: [2]
- unicorn/no-magic-array-flat-depth: [0]
- unicorn/no-negated-condition: [0]
- unicorn/no-negation-in-equality-check: [2]
- unicorn/no-nested-ternary: [0]
- unicorn/no-new-array: [0]
- unicorn/no-new-buffer: [0]
- unicorn/no-null: [0]
- unicorn/no-object-as-default-parameter: [0]
- unicorn/no-process-exit: [0]
- unicorn/no-single-promise-in-promise-methods: [2]
- unicorn/no-static-only-class: [2]
- unicorn/no-thenable: [2]
- unicorn/no-this-assignment: [2]
- unicorn/no-typeof-undefined: [2]
- unicorn/no-unnecessary-await: [2]
- unicorn/no-unnecessary-polyfills: [2]
- unicorn/no-unreadable-array-destructuring: [0]
- unicorn/no-unreadable-iife: [2]
- unicorn/no-unused-properties: [2]
- unicorn/no-useless-fallback-in-spread: [2]
- unicorn/no-useless-length-check: [2]
- unicorn/no-useless-promise-resolve-reject: [2]
- unicorn/no-useless-spread: [2]
- unicorn/no-useless-switch-case: [2]
- unicorn/no-useless-undefined: [0]
- unicorn/no-zero-fractions: [2]
- unicorn/number-literal-case: [0]
- unicorn/numeric-separators-style: [0]
- unicorn/prefer-add-event-listener: [2]
- unicorn/prefer-array-find: [2]
- unicorn/prefer-array-flat-map: [2]
- unicorn/prefer-array-flat: [2]
- unicorn/prefer-array-index-of: [2]
- unicorn/prefer-array-some: [2]
- unicorn/prefer-at: [0]
- unicorn/prefer-blob-reading-methods: [2]
- unicorn/prefer-code-point: [0]
- unicorn/prefer-date-now: [2]
- unicorn/prefer-default-parameters: [0]
- unicorn/prefer-dom-node-append: [2]
- unicorn/prefer-dom-node-dataset: [0]
- unicorn/prefer-dom-node-remove: [2]
- unicorn/prefer-dom-node-text-content: [2]
- unicorn/prefer-event-target: [2]
- unicorn/prefer-export-from: [0]
- unicorn/prefer-global-this: [0]
- unicorn/prefer-includes: [2]
- unicorn/prefer-json-parse-buffer: [0]
- unicorn/prefer-keyboard-event-key: [2]
- unicorn/prefer-logical-operator-over-ternary: [2]
- unicorn/prefer-math-min-max: [2]
- unicorn/prefer-math-trunc: [2]
- unicorn/prefer-modern-dom-apis: [0]
- unicorn/prefer-modern-math-apis: [2]
- unicorn/prefer-module: [2]
- unicorn/prefer-native-coercion-functions: [2]
- unicorn/prefer-negative-index: [2]
- unicorn/prefer-node-protocol: [2]
- unicorn/prefer-number-properties: [0]
- unicorn/prefer-object-from-entries: [2]
- unicorn/prefer-object-has-own: [0]
- unicorn/prefer-optional-catch-binding: [2]
- unicorn/prefer-prototype-methods: [0]
- unicorn/prefer-query-selector: [2]
- unicorn/prefer-reflect-apply: [0]
- unicorn/prefer-regexp-test: [2]
- unicorn/prefer-set-has: [0]
- unicorn/prefer-set-size: [2]
- unicorn/prefer-spread: [0]
- unicorn/prefer-string-raw: [0]
- unicorn/prefer-string-replace-all: [0]
- unicorn/prefer-string-slice: [0]
- unicorn/prefer-string-starts-ends-with: [2]
- unicorn/prefer-string-trim-start-end: [2]
- unicorn/prefer-structured-clone: [2]
- unicorn/prefer-switch: [0]
- unicorn/prefer-ternary: [0]
- unicorn/prefer-text-content: [2]
- unicorn/prefer-top-level-await: [0]
- unicorn/prefer-type-error: [0]
- unicorn/prevent-abbreviations: [0]
- unicorn/relative-url-style: [2]
- unicorn/require-array-join-separator: [2]
- unicorn/require-number-to-fixed-digits-argument: [2]
- unicorn/require-post-message-target-origin: [0]
- unicorn/string-content: [0]
- unicorn/switch-case-braces: [0]
- unicorn/template-indent: [2]
- unicorn/text-encoding-identifier-case: [0]
- unicorn/throw-new-error: [2]
- use-isnan: [2]
- valid-typeof: [2, {requireStringLiterals: true}]
- vars-on-top: [0]
- wc/attach-shadow-constructor: [2]
- wc/define-tag-after-class-definition: [0]
- wc/expose-class-on-global: [0]
- wc/file-name-matches-element: [2]
- wc/guard-define-call: [0]
- wc/guard-super-call: [2]
- wc/max-elements-per-file: [0]
- wc/no-child-traversal-in-attributechangedcallback: [2]
- wc/no-child-traversal-in-connectedcallback: [2]
- wc/no-closed-shadow-root: [2]
- wc/no-constructor-attributes: [2]
- wc/no-constructor-params: [2]
- wc/no-constructor: [2]
- wc/no-customized-built-in-elements: [2]
- wc/no-exports-with-element: [0]
- wc/no-invalid-element-name: [2]
- wc/no-invalid-extends: [2]
- wc/no-method-prefixed-with-on: [2]
- wc/no-self-class: [2]
- wc/no-typos: [2]
- wc/require-listener-teardown: [2]
- wc/tag-name-matches-class: [2]
- yoda: [2, never]
diff --git a/.gitattributes b/.gitattributes
index 9fb4a4e83d..e218bbe25d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,8 +4,7 @@
/assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated
+/options/fileicon/** linguist-generated
/vendor/** -text -eol linguist-vendored
-/web_src/fomantic/build/** linguist-generated
-/web_src/fomantic/_site/globals/site.variables linguist-language=Less
/web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index d37ce219c3..d409f18cd9 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -13,5 +13,5 @@ contact_links:
url: https://docs.gitea.com/help/faq
about: Please check if your question isn't mentioned here.
- name: Crowdin Translations
- url: https://crowdin.com/project/gitea
+ url: https://translate.gitea.com
about: Translations are managed here.
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 46efbcb194..0af43cd029 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -41,7 +41,7 @@ modifies/internal:
- ".dockerignore"
- "docker/**"
- ".editorconfig"
- - ".eslintrc.yaml"
+ - ".eslintrc.cjs"
- ".golangci.yml"
- ".gitpod.yml"
- ".markdownlint.yaml"
@@ -49,7 +49,7 @@ modifies/internal:
- "stylelint.config.js"
- ".yamllint.yaml"
- ".github/**"
- - ".gitea/"
+ - ".gitea/**"
- ".devcontainer/**"
- "build.go"
- "build/**"
@@ -73,9 +73,9 @@ modifies/go:
modifies/frontend:
- changed-files:
- any-glob-to-any-file:
- - "**/*.js"
- - "**/*.ts"
- - "**/*.vue"
+ - "*.js"
+ - "*.ts"
+ - "web_src/**"
docs-update-needed:
- changed-files:
diff --git a/.github/workflows/cron-licenses.yml b/.github/workflows/cron-licenses.yml
index cd8386ecc5..c34066d318 100644
--- a/.github/workflows/cron-licenses.yml
+++ b/.github/workflows/cron-licenses.yml
@@ -1,8 +1,8 @@
name: cron-licenses
on:
- schedule:
- - cron: "7 0 * * 1" # every Monday at 00:07 UTC
+ # schedule:
+ # - cron: "7 0 * * 1" # every Monday at 00:07 UTC
workflow_dispatch:
jobs:
@@ -15,7 +15,7 @@ jobs:
with:
go-version-file: go.mod
check-latest: true
- - run: make generate-license generate-gitignore
+ - run: make generate-gitignore
timeout-minutes: 40
- name: push translations to repo
uses: appleboy/git-push-action@v0.0.3
diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml
index 7c1fb02442..be27537924 100644
--- a/.github/workflows/files-changed.yml
+++ b/.github/workflows/files-changed.yml
@@ -51,14 +51,16 @@ jobs:
- "options/locale/locale_en-US.ini"
frontend:
- - "**/*.js"
+ - "*.js"
+ - "*.ts"
- "web_src/**"
+ - "tools/*.js"
+ - "tools/*.ts"
- "assets/emoji.json"
- "package.json"
- "package-lock.json"
- "Makefile"
- - ".eslintrc.yaml"
- - "stylelint.config.js"
+ - ".eslintrc.cjs"
- ".npmrc"
docs:
@@ -85,6 +87,7 @@ jobs:
swagger:
- "templates/swagger/v1_json.tmpl"
+ - "templates/swagger/v1_input.json"
- "Makefile"
- "package.json"
- "package-lock.json"
diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml
index 7e988e0449..f6720bf2f6 100644
--- a/.github/workflows/pull-compliance.yml
+++ b/.github/workflows/pull-compliance.yml
@@ -37,7 +37,7 @@ jobs:
python-version: "3.12"
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: pip install poetry
@@ -66,7 +66,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -95,7 +95,7 @@ jobs:
go-version-file: go.mod
check-latest: true
- run: make deps-backend deps-tools
- - run: make lint-go-windows lint-go-vet
+ - run: make lint-go-windows lint-go-gitea-vet
env:
TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows
@@ -137,7 +137,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -186,7 +186,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml
index 0b23de0a66..55c2d2bf5e 100644
--- a/.github/workflows/pull-db-tests.yml
+++ b/.github/workflows/pull-db-tests.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
services:
pgsql:
- image: postgres:12
+ image: postgres:14
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
@@ -98,7 +98,7 @@ jobs:
ports:
- "9200:9200"
meilisearch:
- image: getmeili/meilisearch:v1.2.0
+ image: getmeili/meilisearch:v1
env:
MEILI_ENV: development # disable auth
ports:
@@ -202,12 +202,10 @@ jobs:
test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
- # specifying the version of ubuntu in use as mssql fails on newer kernels
- # pending resolution from vendor
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
services:
mssql:
- image: mcr.microsoft.com/mssql/server:2017-latest
+ image: mcr.microsoft.com/mssql/server:2019-latest
env:
ACCEPT_EULA: Y
MSSQL_PID: Standard
diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml
index b84c69e4a0..cc3fbd9c34 100644
--- a/.github/workflows/pull-e2e-tests.yml
+++ b/.github/workflows/pull-e2e-tests.yml
@@ -12,7 +12,9 @@ jobs:
uses: ./.github/workflows/files-changed.yml
test-e2e:
- if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
+ # the "test-e2e" won't pass, and it seems that there is no useful test, so skip
+ # if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
+ if: false
needs: files-changed
runs-on: ubuntu-latest
steps:
@@ -23,7 +25,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend frontend deps-backend
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 2264c9e822..c2cc14f771 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -22,7 +22,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -59,6 +59,8 @@ jobs:
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -85,17 +87,27 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules
run: make vendor
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
+ tags: |-
+ gitea/gitea:${{ steps.clean_name.outputs.branch }}
+ ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
nightly-docker-rootless:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -122,6 +134,12 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules
run: make vendor
- name: build rootless docker image
@@ -131,4 +149,6 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile.rootless
- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
+ tags: |-
+ gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
+ ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml
index a406602dc0..c9c15c31a0 100644
--- a/.github/workflows/release-tag-rc.yml
+++ b/.github/workflows/release-tag-rc.yml
@@ -23,7 +23,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -69,6 +69,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -79,7 +81,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
@@ -90,16 +94,24 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker-rootless:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -110,7 +122,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
@@ -123,11 +137,17 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }}
diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml
index f67b76f408..ae717c7cec 100644
--- a/.github/workflows/release-tag-version.yml
+++ b/.github/workflows/release-tag-version.yml
@@ -14,6 +14,8 @@ concurrency:
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -25,7 +27,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -71,6 +73,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -81,26 +85,34 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# this will generate tags in the following format:
# latest
# 1
# 1.2
# 1.2.3
tags: |
+ type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- type=semver,pattern={{version}}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -116,7 +128,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless,onlatest=true
@@ -126,19 +140,25 @@ jobs:
# 1.2
# 1.2.3
tags: |
+ type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- type=semver,pattern={{version}}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }}
diff --git a/.gitignore b/.gitignore
index 86e6e4fefd..0791a17c71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,11 @@ _test
# IntelliJ
.idea
+.run
+
+# IntelliJ Gateway
+.uuid
+
# Goland's output filename can not be set manually
/go_build_*
/gitea_*
@@ -17,6 +22,9 @@ _test
.vscode
__debug_bin*
+# Visual Studio
+/.vs/
+
*.cgo1.go
*.cgo2.c
_cgo_defun.c
@@ -34,14 +42,10 @@ _testmain.go
coverage.all
cpu.out
-/modules/migration/bindata.go
-/modules/migration/bindata.go.hash
-/modules/options/bindata.go
-/modules/options/bindata.go.hash
-/modules/public/bindata.go
-/modules/public/bindata.go.hash
-/modules/templates/bindata.go
-/modules/templates/bindata.go.hash
+/modules/migration/bindata.*
+/modules/options/bindata.*
+/modules/public/bindata.*
+/modules/templates/bindata.*
*.db
*.log
@@ -79,18 +83,6 @@ cpu.out
/public/assets/fonts
/public/assets/licenses.txt
/vendor
-/web_src/fomantic/node_modules
-/web_src/fomantic/build/*
-!/web_src/fomantic/build/semantic.js
-!/web_src/fomantic/build/semantic.css
-!/web_src/fomantic/build/themes
-/web_src/fomantic/build/themes/*
-!/web_src/fomantic/build/themes/default
-/web_src/fomantic/build/themes/default/assets/*
-!/web_src/fomantic/build/themes/default/assets/fonts
-/web_src/fomantic/build/themes/default/assets/fonts/*
-!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
-!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION
/.air
/.go-licenses
diff --git a/.golangci.yml b/.golangci.yml
index c39d7ac5f2..70efd288ff 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,9 @@
+version: "2"
+output:
+ sort-order:
+ - file
linters:
- enable-all: false
- disable-all: true
- fast: false
+ default: none
enable:
- bidichk
- depguard
@@ -9,139 +11,170 @@ linters:
- errcheck
- forbidigo
- gocritic
- - gofmt
- - gofumpt
- - gosimple
- govet
- ineffassign
+ - mirror
- nakedret
- nolintlint
+ - perfsprint
- revive
- staticcheck
- - stylecheck
- - tenv
- testifylint
- - typecheck
- unconvert
- - unused
- unparam
+ - unused
+ - usestdlibvars
+ - usetesting
- wastedassign
-
-run:
- timeout: 10m
-
-output:
- sort-results: true
- sort-order: [file]
- show-stats: true
-
-linters-settings:
- testifylint:
- disable:
- - go-require
- - require-error
- stylecheck:
- checks: ["all", "-ST1005", "-ST1003"]
- nakedret:
- max-func-lines: 0
- gocritic:
- disabled-checks:
- - ifElseChain
- - singleCaseSwitch # Every time this occurred in the code, there was no other way.
- revive:
- severity: error
+ settings:
+ depguard:
+ rules:
+ main:
+ deny:
+ - pkg: encoding/json
+ desc: use gitea's modules/json instead of encoding/json
+ - pkg: github.com/unknwon/com
+ desc: use gitea's util and replacements
+ - pkg: io/ioutil
+ desc: use os or io instead
+ - pkg: golang.org/x/exp
+ desc: it's experimental and unreliable
+ - pkg: code.gitea.io/gitea/modules/git/internal
+ desc: do not use the internal package, use AddXxx function instead
+ - pkg: gopkg.in/ini.v1
+ desc: do not use the ini package, use gitea's config system instead
+ - pkg: gitea.com/go-chi/cache
+ desc: do not use the go-chi cache package, use gitea's cache system
+ nolintlint:
+ allow-unused: false
+ require-explanation: true
+ require-specific: true
+ gocritic:
+ disabled-checks:
+ - ifElseChain
+ - singleCaseSwitch # Every time this occurred in the code, there was no other way.
+ revive:
+ severity: error
+ rules:
+ - name: atomic
+ - name: bare-return
+ - name: blank-imports
+ - name: constant-logical-expr
+ - name: context-as-argument
+ - name: context-keys-type
+ - name: dot-imports
+ - name: duplicated-imports
+ - name: empty-lines
+ - name: error-naming
+ - name: error-return
+ - name: error-strings
+ - name: errorf
+ - name: exported
+ - name: identical-branches
+ - name: if-return
+ - name: increment-decrement
+ - name: indent-error-flow
+ - name: modifies-value-receiver
+ - name: package-comments
+ - name: range
+ - name: receiver-naming
+ - name: redefines-builtin-id
+ - name: string-of-int
+ - name: superfluous-else
+ - name: time-naming
+ - name: unconditional-recursion
+ - name: unexported-return
+ - name: unreachable-code
+ - name: var-declaration
+ - name: var-naming
+ arguments:
+ - [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
+ - [] # DenyList
+ - - skip-package-name-checks: true # supress errors from underscore in migration packages
+ staticcheck:
+ checks:
+ - all
+ - -ST1003
+ - -ST1005
+ - -QF1001
+ - -QF1006
+ - -QF1008
+ testifylint:
+ disable:
+ - go-require
+ - require-error
+ usetesting:
+ os-temp-dir: true
+ exclusions:
+ generated: lax
+ presets:
+ - comments
+ - common-false-positives
+ - legacy
+ - std-error-handling
rules:
- - name: atomic
- - name: bare-return
- - name: blank-imports
- - name: constant-logical-expr
- - name: context-as-argument
- - name: context-keys-type
- - name: dot-imports
- - name: duplicated-imports
- - name: empty-lines
- - name: error-naming
- - name: error-return
- - name: error-strings
- - name: errorf
- - name: exported
- - name: identical-branches
- - name: if-return
- - name: increment-decrement
- - name: indent-error-flow
- - name: modifies-value-receiver
- - name: package-comments
- - name: range
- - name: receiver-naming
- - name: redefines-builtin-id
- - name: string-of-int
- - name: superfluous-else
- - name: time-naming
- - name: unconditional-recursion
- - name: unexported-return
- - name: unreachable-code
- - name: var-declaration
- - name: var-naming
- gofumpt:
- extra-rules: true
- depguard:
- rules:
- main:
- deny:
- - pkg: encoding/json
- desc: use gitea's modules/json instead of encoding/json
- - pkg: github.com/unknwon/com
- desc: use gitea's util and replacements
- - pkg: io/ioutil
- desc: use os or io instead
- - pkg: golang.org/x/exp
- desc: it's experimental and unreliable
- - pkg: code.gitea.io/gitea/modules/git/internal
- desc: do not use the internal package, use AddXxx function instead
- - pkg: gopkg.in/ini.v1
- desc: do not use the ini package, use gitea's config system instead
- - pkg: gitea.com/go-chi/cache
- desc: do not use the go-chi cache package, use gitea's cache system
-
+ - linters:
+ - dupl
+ - errcheck
+ - gocyclo
+ - gosec
+ - staticcheck
+ - unparam
+ path: _test\.go
+ - linters:
+ - dupl
+ - errcheck
+ - gocyclo
+ - gosec
+ path: models/migrations/v
+ - linters:
+ - forbidigo
+ path: cmd
+ - linters:
+ - dupl
+ text: (?i)webhook
+ - linters:
+ - gocritic
+ text: (?i)`ID' should not be capitalized
+ - linters:
+ - deadcode
+ - unused
+ text: (?i)swagger
+ - linters:
+ - staticcheck
+ text: (?i)argument x is overwritten before first use
+ - linters:
+ - gocritic
+ text: '(?i)commentFormatting: put a space between `//` and comment text'
+ - linters:
+ - gocritic
+ text: '(?i)exitAfterDefer:'
+ paths:
+ - node_modules
+ - public
+ - web_src
+ - third_party$
+ - builtin$
+ - examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
- exclude-dirs: [node_modules, public, web_src]
- exclude-case-sensitive: true
- exclude-rules:
- - path: _test\.go
- linters:
- - gocyclo
- - errcheck
- - dupl
- - gosec
- - unparam
- - staticcheck
- - path: models/migrations/v
- linters:
- - gocyclo
- - errcheck
- - dupl
- - gosec
- - path: cmd
- linters:
- - forbidigo
- - text: "webhook"
- linters:
- - dupl
- - text: "`ID' should not be capitalized"
- linters:
- - gocritic
- - text: "swagger"
- linters:
- - unused
- - deadcode
- - text: "argument x is overwritten before first use"
- linters:
- - staticcheck
- - text: "commentFormatting: put a space between `//` and comment text"
- linters:
- - gocritic
- - text: "exitAfterDefer:"
- linters:
- - gocritic
+formatters:
+ enable:
+ - gofmt
+ - gofumpt
+ settings:
+ gofumpt:
+ extra-rules: true
+ exclusions:
+ generated: lax
+ paths:
+ - node_modules
+ - public
+ - web_src
+ - third_party$
+ - builtin$
+ - examples$
+
+run:
+ timeout: 10m
diff --git a/.ignore b/.ignore
index 5b96dabd38..29912ad5c3 100644
--- a/.ignore
+++ b/.ignore
@@ -1,9 +1,6 @@
*.min.css
*.min.js
/assets/*.json
-/modules/options/bindata.go
-/modules/public/bindata.go
-/modules/templates/bindata.go
/options/gitignore
/options/license
/public/assets
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..88ff1591a4
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Unknwon
+Unknwon æ— é—»
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab8c2ac223..b72ac4849a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,984 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
+## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26
+
+* BREAKING
+ * Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
+ * Improve log format (#33814)
+ * Fix markdown render behaviors (#34122)
+ * Add package version api endpoints (#34173)
+
+* FEATURES
+ * Enforce two-factor auth (2FA: TOTP or WebAuthn) (#34187)
+ * Add fullscreen mode as a more efficient operation way to view projects (#34081)
+ * Add anonymous access support for private/unlisted repositories (#34051)
+ * Support public code/issue access for private repositories (#33127)
+ * Add middleware for request prioritization (#33951)
+ * Add cli flags LDAP group configuration (#33933)
+ * Add file tree to file view page (#32721)
+ * Add material icons for file list (#33837)
+ * Artifacts download api for artifact actions v4 (#33510)
+ * Support choose email when creating a commit via web UI (#33432)
+ * Add basic auth support to rss/atom feeds (#33371)
+ * Add sorting by exclusive labels (issue priority) (#33206)
+ * Add sub issue list support (#32940)
+ * Private README.md for organization (#32872)
+ * Email option to embed images as base64 instead of link (#32061)
+ * Option to delay conflict checking of old pull requests until page view (#27779)
+ * Worktime tracking for the organization level (#19808)
+
+* PERFORMANCE
+ * Add cache for common package queries (#22491)
+ * Move issue pin to an standalone table for querying performance (#33452)
+ * Improve commits list performance to reduce unnecessary database queries (#33528)
+ * Optimize total count of feed when loading activities in user dashboard. (#33841)
+ * Optimize heatmap query (#33853)
+ * Only use prev and next buttons for pagination on user dashboard (#33981)
+ * Improve pull request list API performance (#34052)
+ * Cache GPG keys, emails and users when list commits (#34086)
+ * Refactor Git Attribute & performance optimization (#34154)
+ * Performance optimization for tags synchronization (#34355) #34522
+
+* ENHANCEMENTS
+ * Code
+ * Display when a release attachment was uploaded (#34261)
+ * Support creating relative link to raw path in markdown (#34105)
+ * Improve code block readability and isolate copy button (#34009)
+ * Improve repository commit view (#33877)
+ * Full-file syntax highlighting for diff pages (#33766)
+ * Clone repository with Tea CLI (#33725)
+ * Improve sync fork behavior (#33319)
+ * Make git clone URL could use current signed-in user (#33091)
+ * Add submodule diff links (#33097)
+ * Link to tree views of submodules if possible (#33424)
+ * Only keep popular licenses (#33832)
+ * De-emphasize signed commits (#31160)
+
+ * Actions
+ * Add flat-square action badge style (#34062)
+ * Update action status badge layout (#34018)
+ * Download actions job logs from API (#33858)
+ * Always show the "rerun" button for action jobs (#33692)
+ * Add auto-expanding running actions step (#30058)
+ * Update status check for all supported on.pull_request.types in Gitea (#33117)
+ * Workflow_dispatch use workflow from trigger branch (#33098)
+ * Add action auto-scroll (#30057)
+ * Add workflow_job webhook (#33694)
+ * Add a button editing action secret (#34462)
+
+ * Pull Request
+ * Auto expand "New PR" form (#33971)
+ * Mark parent directory as viewed when all files are viewed (#33958)
+ * Show info about maintainers are allowed to edit a PR (#33738)
+ * Automerge supports deleting branch automatically after merging (#32343)
+ * Add additional command hints for PowerShell & CMD (#33548)
+
+ * Issues
+ * Allow filtering issues by any assignee (#33343)
+ * Show warning on navigation if currently editing comment or title (#32920)
+ * Make tracked time representation display as hours (#33315)
+ * Add No Results Prompt Message on Issue List Page (#33699)
+ * Add sort option recentclose for issues and pulls (#34525) #34539
+
+ * Packages
+ * Link to nuget dependencies (#26554)
+ * Add composor source field (#33502)
+
+ * Administration
+ * Improve navbar: add "admin" tip, add "active" style (#32927)
+ * Add a option "--user-type bot" to admin user create, improve role display (#27885)
+ * Improve admin user view page (#33735)
+ * Support performance trace (#32973)
+ * Change pprof labels to be prometheus compatible (#32865)
+ * Allow admins and org owners to change org member public status (#28294)
+ * Optimize the installation page (#32994)
+ * Make public URL generation configurable (#34250)
+ * Add a --fullname arg to gitea admin user create. (#34241)
+
+ * Others
+ * Improve oauth2 error handling (#33969)
+ * Fail mirroring more gracefully (#34002)
+ * Align User Details Page Header Layout with Design Specifications (#34192)
+ * Webhook add X-Gitea-Hook-Installation-Target-Type Header (#33752)
+ * Optimize the dashboard (#32990)
+ * Improve button layout on small screens (#33633)
+ * Add cropping support when modifying the user/org/repo avatar (#33498)
+ * Make ROOT_URL support using request Host header (#32564)
+ * Add `show more` organizations icon in user's profile (#32986)
+ * Introduce `--page-space-bottom` at 64px (#30692)
+ * Improve theme display (#30671)
+ * Add alphabetical project sorting (#33504)
+ * Add global lock for migrations to make upgrade more safe with multiple replications (#33706)
+ * Add descriptions for private repo public access settings and improve the UI (#34057)
+
+* API
+ * Actions Runner rest api (#33873)
+ * Inclusion of rename organization api (#33303)
+ * Add API to support link package to repository and unlink it (#33481)
+ * Add API endpoint to request contents of multiple files simultaniously (#34139)
+ * Actions artifacts API list/download check status upload confirmed (#34273)
+ * Add API routes to lock and unlock issues (#34165)
+ * Fix some user name usages (#33689)
+ * Allow filtering /repos/{owner}/{repo}/pulls by target base branch queryparam (#33684)
+ * Improve swagger generation (#33664)
+ * Support Ephemeral action runners (#33570)
+ * Support workflow event dispatch via API (#33545)
+ * Support workflow event dispatch via API (#32059)
+ * Added Description Field for Secrets and Variables (#33526)
+ * Reject star-related requests if stars are disabled (#33208)
+ * Let API create and edit system webhooks, attempt 2 (#33180)
+ * Use `Project-URL` metadata field to get a PyPI package's homepage URL (#33089)
+ * Add `last_committer_date` and `last_author_date` for file contents API (#32921)
+
+* REFACTORS
+ * Remove context from git struct (#33793)
+ * Refactor admin/common.ts (#33788)
+ * Refactor repo-settings.ts (#33785)
+ * Refactor repo-issue.ts (#33784)
+ * Small refactor to reduce unnecessary database queries and remove duplicated functions (#33779)
+ * Refactor initRepoBranchTagSelector to use new init framework (#33776)
+ * Refactor buttons to use new init framework (#33774)
+ * Refactor markup and pdf-viewer to use new init framework (#33772)
+ * Refactor error system (#33771)
+ * Refactor mail code (#33768)
+ * Update TypeScript types (#33799)
+ * Refactor older tests to use testify (#33140)
+ * Move notifywatch to service layer (#33825)
+ * Decouple context from repository related structs (#33823)
+ * Remove context from mail struct (#33811)
+ * Refactor dropdown ellipsis (#34123)
+ * Refactor functions to reduce repopath expose (#33892)
+ * Refactor repo-diff.ts (#33746)
+ * Refactor web route handler (#33488)
+ * Refactor user & avatar (#33433)
+ * Refactor user package (#33423)
+ * Refactor decouple context from migration structs (#33399)
+ * Refactor context flash msg and global variables (#33375)
+ * Refactor response writer & access logger (#33323)
+ * Refactor ref type (#33242)
+ * Refactor context repository (#33202)
+ * Refactor legacy JS (#33115)
+ * Refactor legacy line-number and scroll code (#33094)
+ * Refactor env var related code (#33075)
+ * Move SetMerged to service layer (#33045)
+ * Merge updatecommentattachment functions (#33044)
+ * Refactor pull-request compare&create page (#33071)
+ * Refactor repo-new.ts (#33070)
+ * Refactor pagination (#33037)
+ * Refactor tests (#33021)
+ * Refactor markup render to fix various path problems (#34114)
+ * Refactor Branch struct in package modules/git (#33980)
+ * Don't create duplicated functions for code repositories and wiki repositories (#33924)
+ * Move git references checking to gitrepo packages to reduce expose of repository path (#33891)
+ * Refactor cache-control (#33861)
+ * Decouple diff stats query from actual diffing (#33810)
+ * Move part of updating protected branch logic to service layer (#33742)
+ * Decouple Batch from git.Repository to simplify usage without requiring the creation of a Repository struct. (#34001)
+ * Refactor tmpl and blob_excerpt (#32967)
+ * Refactor template & test related code (#32938)
+ * Refactor db package and remove unnecessary `DumpTables` (#32930)
+ * Refactor pprof labels and process desc (#32909)
+ * Refactor repo-projects.ts (#32892)
+ * Refactor getpatch/getdiff functions and remove unnecessary fallback (#32817)
+ * Uniform all temporary directories and allow customizing temp path (#32352)
+ * Remove context from retry downloader (#33871)
+ * Refactor global init code and add more comments (#33755)
+ * Remove some unnecessary template helpers (#33069)
+ * Move and rename UpdateRepository (#34136)
+ * Move hooks function to gitrepo and reduce expose repopath (#33890)
+ * Add abstraction layer to delete repository from disk (#33879)
+ * Add abstraction layer to check if the repository exists on disk (#33874)
+ * Move ParseCommitWithSSHSignature to service layer (#34087)
+ * Move duplicated functions (#33977)
+ * Extract code to their own functions for push update (#33944)
+ * Move gitgraph from modules to services layer (#33527)
+ * Move commits signature and verify functions to service layers (#33605)
+ * Use `CloseIssue` and `ReopenIssue` instead of `ChangeStatus` (#32467)
+ * Refactor arch route handlers (#32993)
+ * Refactor "string truncate" (#32984)
+ * Refactor arch route handlers (#32972)
+ * Clarify path param naming (#32969)
+ * Refactor request context (#32956)
+ * Move some errors to their own sub packages (#32880)
+ * Move RepoTransfer from models to models/repo sub package (#32506)
+ * Move delete deploy keys into service layer (#32201)
+ * Refactor webhook events (#33337)
+ * Move some Actions related functions from `routers` to `services` (#33280)
+ * Refactor RefName (#33234)
+ * Refactor context RefName and RepoAssignment (#33226)
+ * Refactor repository transfer (#33211)
+ * Refactor error system (#33626)
+ * Refactor error system (#33610)
+ * Refactor package (routes and error handling, npm peer dependency) (#33111)
+ * Use test context in tests and new loop system in benchmarks (#33648)
+ * Some small refactors (#33144)
+ * Simplify context ref name (#33267)
+
+* BUGFIXES
+ * Fix some dropdown problems on the issue sidebar (#34308) #34327
+ * Do not return archive download URLs in API if downloads are disabled (#34324) #34338
+ * Fix LFS files being editable in web UI (#34356) #34362
+ * Fix only text/* being viewable in web UI (#34374) #34378
+ * Fix LFS file not stored in LFS when uploaded/edited via API or web UI (#34367)
+ * Grey out expired artifact on Artifacts list (#34314) #34404
+ * Fix incorrect divergence cache after switching default branch (#34370) #34406
+ * Refactor commit message rendering and fix bugs (#34412) #34414
+ * Merge and tweak markup editor expander CSS (#34409) #34415
+ * Fix GetUsersByEmails (#34423) #34425
+ * Only git operations should update last changed of a repository (#34388) #34427
+ * Fix comment textarea scroll issue in Firefox (#34438) #34446
+ * Fix repo broken check (#34444) #34452
+ * Fix remove org user failure on mssql (#34449) #34453
+ * Fix Workflow run Not Found page (#34459) #34466
+ * When updating comment, if the content is the same, just return and not update the database (#34422) #34464
+ * Fix project board view (#34470) #34475
+ * Fix get / delete runner to use consistent http 404 and 500 status (#34480) #34488
+ * Fix url validation in webhook add/edit API (#34492) #34496
+ * Fix edithook api can not update package, status and workflow_job events (#34495) #34499
+ * Fix ephemeral runner deletion (#34447) #34513
+ * Don't display error log when .git-blame-ignore-revs doesn't exist (#34457)
+ * Only allow admins to rename default/protected branches (#33276)
+ * Improve "lock conversation" UI (#34207)
+ * Fix incorrect file links (#34189)
+ * Optimize Overflow Menu (#34183)
+ * Check user/org repo limit instead of doer (#34147)
+ * Make markdown render match GitHub's behavior (#34129)
+ * Fix team permission (#34128)
+ * Correctly handle submodule view and avoid throwing 500 error (#34121)
+ * Fix users being able bypass limits with repo transfers (#34031)
+ * Avoid creating unnecessary temporary cat file sub process (#33942)
+ * Refactor organization menu (#33928)
+ * Fix various Fomantic UI and htmx problems (#33851)
+ * Fix 500 error when error occurred in migration page (#33256)
+ * Validate that the tag doesn't exist when creating a tag via the web (#33241)
+ * Add missed transaction on setmerged (#33079)
+ * Rework create/fork/adopt/generate repository to make sure resources will be cleanup once failed (#31035)
+ * Valid email address should only start with alphanumeric (#28174)
+ * Fix webhook url (#34186)
+ * Fix "toAbsoluteLocaleDate" test when system locale is not en-US (#33939)
+ * Fix file name could not be searched if the file was not a text file when using the Bleve indexer (#33959)
+ * Fix cannot delete runners via the modal dialog (#33895)
+ * Fix unpin hint on the pinned pull requests (#33207)
+ * Fix parentCommit invalid memory address or nil pointer dereference. (#33204)
+ * Fix comment header padding (#33377)
+ * Fix some migration and repo name problems (#33986)
+ * Fix various trivial frontend problems (#34263)
+ * Fix Set Email Preference dropdown and button placement (#34255)
+ * Fix quoted replies incorrectly render user input as part of the quote (#34216)
+ * Fix button alignments and remove unnecessary styles (#34206)
+ * Restore form inputs on organization create error (#34201)
+ * Try to fix ACME (3rd) (#33807)
+ * Fix incorrect ref "blob" (#33240)
+ * Fix dynamic content loading init problem (#33748)
+ * Fix git empty check and HEAD request (#33690)
+ * Fix Untranslated Text on Actions Page (#33635)
+ * Fix issue label delete incorrect labels webhook payload (#34575)
+ * Fix incorrect page navigation with up and down arrow on last item of dashboard repos (#34570)
+ * Fix/improve avatar sync from LDAP (#34573)
+ * Fix some trivial problems (#34579)
+ * Retain issue sort type when a keyword search is introduced (#34559)
+ * Always use an empty line to separate the commit message and trailer (#34512)
+ * Fix line-button issue after file selection in file tree (#34574)
+ * Fix doctor deleting orphaned issues attachments (#34142)
+ * Add webhook assigning test and fix possible bug (#34420)
+ * Fix possible nil description of pull request when migrating from CodeCommit (#34541)
+ * Refactor commit reader (#34542)
+ * Fix possible pull request broken when leave the page immediately after clicking the update button #34509
+ * Ignore "Close" error when uploading container blob (#34620)
+ * Fix missed merge commit sha and time when migrating from codecommit (#34645)
+ * Fix GetUsersByEmails (#34643)
+ * Misc CSS fixes (#34638)
+ * Add codecommit to supported services in api docs (#34626)
+ * Validate hex colors when creating/editing labels (#34623)
+ * Fix possible pull request broken when leave the page immediately after clicking the update button (#34509)
+ * Fix margin issue in markup paragraph rendering (#34599)
+ * Fix migration pull request title too long (#34577)
+ * Fix footnote jump behavior on the issue page. (#34621)
+ * Fix "oras" OCI client compatibility (#34666)
+ * Fix last admin check when syncing users (#34649)
+ * Fix skip paths check on tag push events in workflows (#34602) #34670
+
+* MISC
+
+ * Bump to alpine 3.22 (#34613)
+ * Make pull request and issue history more compact (#34588)
+ * Run integration tests against postgres 14 (#34514) #34536
+ * Enable addtional linters (#34085)
+ * Enable testifylint rules (#34075)
+ * Enable staticcheck QFxxxx rules (#34064)
+ * Improve Actions test (#32883)
+ * Drop fomantic build (#33845)
+ * Go1.24 (#33562)
+ * Run yamllint with strict mode, fix issue (#33551)
+ * Disable cron task to update license (#33486)
+ * Optimize makefile help information generation (#33390)
+ * Convert github.com/xanzy/go-gitlab into gitlab.com/gitlab-org/api/client-go (#33126)
+ * Add missed changelogs (#33649)
+ * Update .changelog file to add performance label group (#33472)
+ * Add missing POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES in app.example.ini (#33363)
+ * Update README screenshots (#33347)
+ * Update unrs-resolver (#34279)
+ * Update go&js dependencies (#34262)
+ * Optimize the calling code of queryElems (#34235)
+ * Update protected_branch.tmpl (#34193)
+ * Feat/optimize span svg layout (#34185)
+ * Set MERMAID_MAX_SOURCE_CHARACTERS to 50000 (#34152)
+ * Update JS and PY deps (#34143)
+ * Add Chinese translations for README files (#34132)
+ * Use `overflow-wrap: anywhere` to replace `word-break: break-all` (#34126)
+ * Clarify ownership in password change error messages (#34092)
+ * Add toggleClass function in dom.ts (#34063)
+ * Update to golangci-lint v2 (#34054)
+ * Update Makefile test comments (#34013)
+ * Update go mod dependencies (#33988)
+ * Use filepath.Join instead of path.Join for file system file operations (#33978)
+ * Prepare common tmpl functions in a middleware (#33957)
+ * Remove unused or abused styles (#33918)
+ * Update JS and PY deps, misc tweaks (#33903)
+ * Try to figure out attribute checker problem (#33901)
+ * Add lock for a repository pull mirror (#33876)
+ * Fine tune push mirror UI (#33866)
+ * Improve issue & code search (#33860)
+ * Use pullrequestlist instead of []*pullrequest (#33765)
+ * Upgrade act to 0.261.4 and actions-proto-go to v0.4.1 (#33760)
+ * Align sidebar gears to the right (#33721)
+ * Update Go dependencies (skip blevesearch, meilisearch) (#33655)
+ * Add migrations and doctor fixes (#33556)
+ * Remove "class-name" from svg icon (#33540)
+ * Update MAINTAINERS (#33529)
+ * Add "No data available" display when list is empty (#33517)
+ * Use `git diff-tree` for `DiffFileTree` on diff pages (#33514)
+ * Give organisation members access to organisation feeds (#33508)
+ * Update feishu icon (#33470)
+ * Hide/disable unusable UI elements when a repository is archived (#33459)
+ * Update `@github/text-expander-element` to 2.9.0 (#33435)
+ * Do not access GitRepo when a repo is being created (#33380)
+ * Fix incorrect ref usages (#33301)
+ * Prepare for support performance trace (#33286)
+ * Enable Typescript `noImplicitThis` (#33250)
+ * Remove unused CSS styles and move some styles to proper files (#33217)
+ * Add .run to gitignore (#33175)
+ * Fix typo in gitea downloader test and add missing codebase in `ToGitServiceType` (#33146)
+ * Remove extended glob pattern from branch protection UI (#33125)
+ * Clean up legacy form CSS styles (#33081)
+ * Unset XDG_HOME_CONFIG as gitea manages configuration locations (#33067)
+ * Add IntelliJ Gateway's .uuid to gitignore (#33052)
+ * User facing messages for AGit errors (#33012)
+ * Always show assignees on right (#33006)
+ * Fix eslint (#33002)
+ * Update JS dependencies (#32914)
+ * Bump x/net (#32896) (#32900)
+ * Only activity tab needs heatmap data loading (#34652)
+
+## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/1.23.8) - 2025-05-11
+
+* SECURITY
+ * Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
+ * Update net package (#34228) (#34232)
+* BUGFIXES
+ * Fix releases sidebar navigation link (#34436) #34439
+ * Fix bug webhook milestone is not right. (#34419) #34429
+ * Fix two missed null value checks on the wiki page. (#34205) (#34215)
+ * Swift files can be passed either as file or as form value (#34068) (#34236)
+ * Fix bug when API get pull changed files for deleted head repository (#34333) (#34368)
+ * Upgrade github v61 -> v71 to fix migrating bug (#34389)
+ * Fix bug when visiting comparation page (#34334) (#34364)
+ * Fix wrong review requests when updating the pull request (#34286) (#34304)
+ * Fix github migration error when using multiple tokens (#34144) (#34302)
+ * Explicitly not update indexes when sync database schemas (#34281) (#34295)
+ * Fix panic when comment is nil (#34257) (#34277)
+ * Fix project board links to related Pull Requests (#34213) (#34222)
+ * Don't assume the default wiki branch is master in the wiki API (#34244) (#34245)
+* DOCUMENTATION
+ * Update token creation API swagger documentation (#34288) (#34296)
+* MISC
+ * Fix CI Build (#34315)
+ * Add riscv64 support (#34199) (#34204)
+ * Bump go version in go.mod (#34160)
+ * remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158)
+
+## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/1.23.7) - 2025-04-07
+
+* Enhancements
+ * Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
+ * Also check default ssh-cert location for host (#34099) (#34100) (#34116)
+* BUGFIXES
+ * Fix discord webhook 400 status code when description limit is exceeded (#34084) (#34124)
+ * Get changed files based on merge base when checking `pull_request` actions trigger (#34106) (#34120)
+ * Fix invalid version in RPM package path (#34112) (#34115)
+ * Return default avatar url when user id is zero rather than updating database (#34094) (#34095)
+ * Add additional ReplaceAll in pathsep to cater for different pathsep (#34061) (#34070)
+ * Try to fix check-attr bug (#34029) (#34033)
+ * Git client will follow 301 but 307 (#34005) (#34010)
+ * Fix block expensive for 1.23 (#34127)
+ * Fix markdown frontmatter rendering (#34102) (#34107)
+ * Add new CLI flags to set name and scopes when creating a user with access token (#34080) (#34103)
+ * Do not show 500 error when default branch doesn't exist (#34096) (#34097)
+ * Hide activity contributors, recent commits and code frequrency left tabs if there is no code permission (#34053) (#34065)
+ * Simplify emoji rendering (#34048) (#34049)
+ * Adjust the layout of the toolbar on the Issues/Projects page (#33667) (#34047)
+ * Pull request updates will also trigger code owners review requests (#33744) (#34045)
+ * Fix org repo creation being limited by user limits (#34030) (#34044)
+ * Fix git client accessing renamed repo (#34034) (#34043)
+ * Fix the issue with error message logging for the `check-attr` command on Windows OS. (#34035) (#34036)
+ * Polyfill WeakRef (#34025) (#34028)
+
+## [1.23.6](https://github.com/go-gitea/gitea/releases/tag/v1.23.6) - 2025-03-24
+
+* SECURITY
+ * Fix LFS URL (#33840) (#33843)
+ * Update jwt and redis packages (#33984) (#33987)
+ * Update golang crypto and net (#33989)
+* BUGFIXES
+ * Drop timeout for requests made to the internal hook api (#33947) (#33970)
+ * Fix maven panic when no package exists (#33888) (#33889)
+ * Fix markdown render (#33870) (#33875)
+ * Fix auto concurrency cancellation skips commit status updates (#33764) (#33849)
+ * Fix oauth2 auth (#33961) (#33962)
+ * Fix incorrect 1.23 translations (#33932)
+ * Try to figure out attribute checker problem (#33901) (#33902)
+ * Ignore trivial errors when updating push data (#33864) (#33887)
+ * Fix some UI problems for 1.23 (#33856)
+ * Removing unwanted ui container (#33833) (#33835)
+ * Support disable passkey auth (#33348) (#33819)
+ * Do not call "git diff" when listing PRs (#33817)
+ * Try to fix ACME (3rd) (#33807) (#33808)
+ * Fix incorrect code search indexer options (#33992) #33999
+
+## [1.23.5](https://github.com/go-gitea/gitea/releases/tag/v1.23.5) - 2025-03-04
+
+* SECURITY
+ * Bump x/oauth2 & x/crypto (#33704) (#33727)
+* PERFORMANCE
+ * Optimize user dashboard loading (#33686) (#33708)
+* BUGFIXES
+ * Fix navbar dropdown item align (#33782)
+ * Fix inconsistent closed issue list icon (#33722) (#33728)
+ * Fix for Maven Package Naming Convention Handling (#33678) (#33679)
+ * Improve Open-with URL encoding (#33666) (#33680)
+ * Deleting repository should unlink all related packages (#33653) (#33673)
+ * Fix omitempty bug (#33663) (#33670)
+ * Upgrade go-crypto from 1.1.4 to 1.1.6 (#33745) (#33754)
+ * Fix OCI image.version annotation for releases to use full semver (#33698) (#33701)
+ * Try to fix ACME path when renew (#33668) (#33693)
+ * Fix mCaptcha bug (#33659) (#33661)
+ * Git graph: don't show detached commits (#33645) (#33650)
+ * Use MatchPhraseQuery for bleve code search (#33628)
+ * Adjust appearence of commit status webhook (#33778) #33789
+ * Upgrade golang net from 0.35.0 -> 0.36.0 (#33795) #33796
+
+## [1.23.4](https://github.com/go-gitea/gitea/releases/tag/v1.23.4) - 2025-02-16
+
+* SECURITY
+ * Enhance routers for the Actions variable operations (#33547) (#33553)
+ * Enhance routers for the Actions runner operations (#33549) (#33555)
+ * Fix project issues list and counting (#33594) #33619
+* PERFORMANCES
+ * Performance optimization for pull request files loading comments attachments (#33585) (#33592)
+* BUGFIXES
+ * Add a transaction to `pickTask` (#33543) (#33563)
+ * Fix mirror bug (#33597) (#33607)
+ * Use default Git timeout when checking repo health (#33593) (#33598)
+ * Fix PR's target branch dropdown (#33589) (#33591)
+ * Fix various problems (artifact order, api empty slice, assignee check, fuzzy prompt, mirror proxy, adopt git) (#33569) (#33577)
+ * Rework suggestion backend (#33538) (#33546)
+ * Fix context usage (#33554) (#33557)
+ * Only show the latest version in the Arch index (#33262) (#33580)
+ * Skip deletion error for action artifacts (#33476) (#33568)
+ * Make actions URL in commit status webhooks absolute (#33620) #33632
+ * Add missing locale (#33641) #33642
+
+## [1.23.3](https://github.com/go-gitea/gitea/releases/tag/v1.23.3) - 2025-02-06
+
+* Security
+ * Build Gitea with Golang v1.23.6 to fix security bugs
+* BUGFIXES
+ * Fix a bug caused by status webhook template #33512
+
+## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04
+
+* BREAKING
+ * Add tests for webhook and fix some webhook bugs (#33396) (#33442)
+ * Package webhook’s Organization was incorrectly used as the User struct. This PR fixes the issue.
+ * This changelog is just a hint. The change is not really breaking because most fields are the same, most users are not affected.
+* ENHANCEMENTS
+ * Clone button enhancements (#33362) (#33404)
+ * Repo homepage styling tweaks (#33289) (#33381)
+ * Add a confirm dialog for "sync fork" (#33270) (#33273)
+ * Make tracked time representation display as hours (#33315) (#33334)
+ * Improve sync fork behavior (#33319) (#33332)
+* BUGFIXES
+ * Fix code button alignment (#33345) (#33351)
+ * Correct bot label `vertical-align` (#33477) (#33480)
+ * Fix SSH LFS memory usage (#33455) (#33460)
+ * Fix issue sidebar dropdown keyboard support (#33447) (#33450)
+ * Fix user avatar (#33439)
+ * Fix `GetCommitBranchStart` bug (#33298) (#33421)
+ * Add pubdate for repository rss and add some tests (#33411) (#33416)
+ * Add missed auto merge feed message on dashboard (#33309) (#33405)
+ * Fix issue suggestion bug (#33389) (#33391)
+ * Make issue suggestion work for all editors (#33340) (#33342)
+ * Fix issue count (#33338) (#33341)
+ * Fix Account linking page (#33325) (#33327)
+ * Fix closed dependency title (#33285) (#33287)
+ * Fix sidebar milestone link (#33269) (#33272)
+ * Fix missing license when sync mirror (#33255) (#33258)
+ * Fix upload file form (#33230) (#33233)
+ * Fix mirror bug (#33224) (#33225)
+ * Fix system admin cannot fork or get private fork with API (#33401) (#33417)
+ * Fix push message behavior (#33215) (#33317)
+ * Trivial fixes (#33304) (#33312)
+ * Fix "stop time tracking button" on navbar (#33084) (#33300)
+ * Fix tag route and empty repo (#33253)
+ * Fix cache test triggered by non memory cache (#33220) (#33221)
+ * Revert empty lfs ref name (#33454) (#33457)
+ * Fix flex width (#33414) (#33418)
+ * Fix commit status events (#33320) #33493
+ * Fix unnecessary comment when moving issue on the same project column (#33496) #33499
+ * Add timetzdata build tag to binary releases (#33463) #33503
+* MISC
+ * Use ProtonMail/go-crypto to replace keybase/go-crypto (#33402) (#33410)
+ * Update katex to latest version (#33361)
+ * Update go tool dependencies (#32916) (#33355)
+
+## [1.23.1](https://github.com/go-gitea/gitea/releases/tag/v1.23.1) - 2025-01-09
+
+* ENHANCEMENTS
+ * Move repo size to sidebar (#33155) (#33182)
+* BUGFIXES
+ * Use updated path to s6-svscan after alpine upgrade (#33185) (#33188)
+ * Fix fuzz test (#33156) (#33158)
+ * Fix raw file API ref handling (#33172) (#33189)
+ * Fix ACME panic (#33178) (#33186)
+ * Fix branch dropdown not display ref name (#33159) (#33183)
+ * Fix assignee list overlapping in Issue sidebar (#33176) (#33181)
+ * Fix sync fork for consistency (#33147) #33192
+ * Fix editor markdown not incrementing in a numbered list (#33187) #33193
+
+## [1.23.0](https://github.com/go-gitea/gitea/releases/tag/v1.23.0) - 2025-01-08
+
+* BREAKING
+ * Rename config option `[camo].Allways` to `[camo].Always` (#32097)
+ * Remove SHA1 for support for ssh rsa signing (#31857)
+ * Use UTC as default timezone when schedule Actions cron tasks (#31742)
+ * Delete Actions logs older than 1 year by default (#31735)
+ * Make OIDC introspection authentication strictly require Client ID and secret (#31632)
+
+* SECURITY
+ * Include file extension checks in attachment API (#32151)
+ * Include all security fixes which have been backported to v1.22
+
+* FEATURES
+ * Allow to fork repository into the same owner (#32819)
+ * Support "merge upstream branch" (Sync fork) (#32741)
+ * Add Arch package registry (#32692)
+ * Allow to disable the password-based login (sign-in) form (#32687)
+ * Allow cropping an avatar before setting it (#32565)
+ * Support quote selected comments to reply (#32431)
+ * Add reviewers selection to new pull request (#32403)
+ * Suggestions for issues (#32327)
+ * Add priority to protected branch (#32286)
+ * Included tag search capabilities (#32045)
+ * Add option to filter board cards by labels and assignees (#31999)
+ * Add automatic light/dark option for the colorblind theme (#31997)
+ * Support migration from AWS CodeCommit (#31981)
+ * Introduce globallock as distributed locks (#31908 & #31813)
+ * Support compression for Actions logs & enable by default (#31761 & #32013)
+ * Add pure SSH LFS support (#31516)
+ * Add Passkey login support (#31504)
+ * Actions support workflow dispatch event (#28163)
+ * Support repo license (#24872)
+ * Issue time estimate, meaningful time tracking (#23113)
+ * GitHub like repo home page (#32213 & #32847)
+ * Rearrange Clone Panel (#31142)
+ * Enhancing Gitea OAuth2 Provider with Granular Scopes for Resource Access (#32573)
+ * Use env GITEA_RUNNER_REGISTRATION_TOKEN as global runner token (#32946) #32964
+ * Update i18n.go - Language Picker (#32933) #32935
+
+* PERFORMANCE
+ * Perf: add extra index to notification table (#32395)
+ * Introduce OrgList and add LoadTeams, optimaze Load teams for orgs (#32543)
+ * Improve performance of diffs (#32393)
+ * Make LFS http_client parallel within a batch. (#32369)
+ * Add new index for action to resolve the performance problem (#32333)
+ * Improve get feed with pagination (#31821)
+ * Performance improvements for pull request list API (#30490)
+ * Use batch database operations instead of one by one to optimze api pulls (#32680)
+ * Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987) #33046
+
+* ENHANCEMENTS
+ * Code
+ * Remove unnecessary border in repo home page sidebar (#32767)
+ * Add 'Copy path' button to file view (#32584)
+ * Improve diff file tree (#32658)
+ * Add new [lfs_client].BATCH_SIZE and [server].LFS_MAX_BATCH_SIZE config settings. (#32307)
+ * Updated tokenizer to better matching when search for code snippets (#32261)
+ * Change the code search to sort results by relevance (#32134)
+ * Support migrating GitHub/GitLab PR draft status (#32242)
+ * Move lock icon position and add additional tooltips to branch list page (#31839)
+ * Add tag name in the commits list (#31082)
+ * Add `MAX_ROWS` option for CSV rendering (#30268)
+ * Allow code search by filename (#32210)
+ * Make git push options accept short name (#32245)
+ * Repo file list enhancements (#32835)
+
+ * Markdown & Editor
+ * Refactor markdown math render, add dollor-backquote syntax support (#32831)
+ * Make Monaco theme follow browser, fully type codeeditor.ts (#32756)
+ * Refactor markdown editor and use it for milestone description editor (#32688)
+ * Add some handy markdown editor features (#32400)
+ * Improve markdown textarea for indentation and lists (#31406)
+
+ * Issue
+ * Add label/author/assignee filters to the user/org home issue list (#32779)
+ * Refactor issue filter (labels, poster, assignee) (#32771)
+ * Style unification for the issue_management area (#32605)
+ * Add "View all branches/tags" entry to Branch Selector (#32653)
+ * Improve textarea paste (#31948)
+ * Add avif image file support (#32508)
+ * Prevent from submitting issue/comment on uploading (#32263)
+ * Issue Templates: add option to have dropdown printed list (#31577)
+ * Allow searching issues by ID (#31479)
+ * Add `is_archived` option for issue indexer (#32735)
+ * Improve attachment upload methods (#30513)
+ * Support issue template assignees (#31083)
+ * Prevent simultaneous editing of comments and issues (#31053)
+ * Add issue comment when moving issues from one column to another of the project (#29311)
+
+ * Pull Request
+ * Display head branch more comfortable on pull request view (#32000)
+ * Simplify review UI (#31062)
+ * Allow force push to protected branches (#28086)
+ * Add line-through for deleted branch on pull request view page (#32500)
+ * Support requested_reviewers data in comment webhook events (#26178)
+ * Allow maintainers to view and edit files of private repos when "Allow maintainers to edit" is enabled (#32215)
+ * Allow including `Reviewed-on`/`Reviewed-by` lines for custom merge messages (#31211)
+
+ * Actions
+ * Render job title as commit message (#32748)
+ * Refactor RepoActionView.vue, add `::group::` support (#32713)
+ * Make RepoActionView.vue support `##[group]` (#32770)
+ * Support `pull_request_target` event for commit status (#31703)
+ * Detect whether action view branch was deleted (#32764)
+ * Allow users with write permission to run actions (#32644)
+ * Show latest run when visit /run/latest (#31808)
+
+ * Packages
+ * Improve rubygems package registry (#31357)
+ * Add support for npm bundleDependencies (#30751)
+ * Add signature support for the RPM module (#27069)
+ * Extract and display readme and comments for Composer packages (#30927)
+
+ * Project
+ * Add title to project view page (#32747)
+ * Set the columns height to hug all its contents (#31726)
+ * Rename project `board` -> `column` to make the UI less confusing (#30170)
+
+ * User & Organazition
+ * Use better name for userinfo structure (#32544)
+ * Use user.FullName in Oauth2 id_token response (#32542)
+ * Limit org member view of restricted users (#32211)
+ * Allow disabling authentication related user features (#31535)
+ * Add option to change mail from user display name (#31528)
+ * Use FullName in Emails to address the recipient if possible (#31527)
+
+ * Administration
+ * Add support for a credentials chain for minio access (#31051)
+ * Move admin routers from /admin to /-/admin (#32189)
+ * Add cache test for admins (#31265)
+ * Add option for mailer to override mail headers (#27860)
+ * Azure blob storage support (#30995)
+ * Supports forced use of S3 virtual-hosted style (#30969)
+ * Move repository visibility to danger zone in the settings area (#31126)
+
+ * Others
+ * Remove urls from translations (#31950)
+ * Simplify 404/500 page (#31409)
+ * Optimize installation-page experience (#32558)
+ * Refactor login page (#31530)
+ * Add new event commit status creation and webhook implementation (#27151)
+ * Repo Activity: count new issues that were closed (#31776)
+ * Set manual `tabindex`es on login page (#31689)
+ * Add `YEAR`, `MONTH`, `MONTH_ENGLISH`, `DAY` variables for template repos (#31584)
+ * Add typescript guideline and typescript-specific eslint plugins and fix issues (#31521)
+ * Make toast support preventDuplicates (#31501)
+ * Fix tautological conditions (#30735)
+ * Issue change title notifications (#33050) #33065
+
+* API
+ * Implement update branch API (#32433)
+ * Fix missing outputs for jobs with matrix (#32823)
+ * Make API "compare" accept commit IDs (#32801)
+ * Add github compatible tarball download API endpoints (#32572)
+ * Harden runner updateTask and updateLog api (#32462)
+ * Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages and fix an issue related to user search (#32288)
+ * Make admins adhere to branch protection rules (#32248)
+ * Calculate `PublicOnly` for org membership only once (#32234)
+ * Allow filtering PRs by poster in the ListPullRequests API (#32209)
+ * Return 404 instead of error when commit not exist (#31977)
+ * Save initial signup information for users to aid in spam prevention (#31852)
+ * Fix upload maven pacakge parallelly (#31851)
+ * Fix null requested_reviewer from API (#31773)
+ * Add permission description for API to add repo collaborator (#31744)
+ * Add return type to GetRawFileOrLFS and GetRawFile (#31680)
+ * Add skip secondary authorization option for public oauth2 clients (#31454)
+ * Add tag protection via rest api #17862 (#31295)
+ * Document possible action types for the user activity feed API (#31196)
+ * Add topics for repository API (#31127)
+ * Add support for searching users by email (#30908)
+ * Add API endpoints for getting action jobs status (#26673)
+
+* REFACTOR
+ * Update JS and PY dependencies (#31940)
+ * Enable `no-jquery/no-parse-html-literal` and fix violation (#31684)
+ * Refactor image diff (#31444)
+ * Refactor CSRF token (#32216)
+ * Fix some typescript issues (#32586)
+ * Refactor names (#31405)
+ * Use per package global lock for container uploads instead of memory lock (#31860)
+ * Move team related functions to service layer (#32537)
+ * Move GetFeeds to service layer (#32526)
+ * Resolve lint for unused parameter and unnecessary type arguments (#30750)
+ * Reimplement GetUserOrgsList to make it simple and clear (#32486)
+ * Move some functions from issue.go to standalone files (#32468)
+ * Refactor sidebar assignee&milestone&project selectors (#32465)
+ * Refactor sidebar label selector (#32460)
+ * Fix a number of typescript issues (#32459)
+ * Refactor language menu and dom utils (#32450)
+ * Refactor issue page info (#32445)
+ * Split issue sidebar into small templates (#32444)
+ * Refactor template ctx and render utils (#32422)
+ * Refactor repo legacy (#32404)
+ * Refactor markup package (#32399)
+ * Refactor markup render system (#32533 & #32589 & #32612)
+ * Refactor the DB migration system slightly (#32344)
+ * Remove jQuery import from some files (#32512)
+ * Strict pagination check (#32548)
+ * Split mail sender sub package from mailer service package (#32618)
+ * Remove outdated code about fixture generation (#32708)
+ * Refactor RepoBranchTagSelector (#32681)
+ * Refactor issue list (#32755)
+ * Refactor LabelEdit (#32752)
+ * Split issue/pull view router function as multiple smaller functions (#32749)
+ * Refactor some LDAP code (#32849)
+ * Unify repo search order by logic (#30876)
+ * Remove duplicate empty repo check in delete branch API (#32569)
+ * Replace deprecated `math/rand` functions (#30733)
+ * Remove fomantic dimmer module (#30723)
+ * Add types to fetch,toast,bootstrap,svg (#31627)
+ * Refactor webhook (#31587)
+ * Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
+ * Refactor RepoRefByType (#32413)
+ * Refactor: remove redundant err declarations (#32381)
+ * Refactor markup code (#31399)
+ * Refactor render system (orgmode) (#32671)
+ * Refactor render system (#32492)
+ * Refactor markdown render (#32736 & #32728)
+ * Refactor repo unit "disabled" check (#31389)
+ * Refactor route path normalization (#31381)
+ * Refactor to use UnsafeStringToBytes (#31358)
+ * Migrate vue components to setup (#32329)
+ * Refactor globallock (#31933)
+ * Use correct function name (#31887)
+ * Use a common message template instead of a special one (#31878)
+ * Fix a number of Typescript issues (#31877)
+ * Refactor dropzone (#31482)
+ * Move custom `tw-` helpers to tailwind plugin (#31184)
+ * Replace `gt-word-break` with `tw-break-anywhere` (#31183)
+ * Drop `IDOrderDesc` for listing Actions task and always order by `id DESC` (#31150)
+ * Split common-global.js into separate files (#31438)
+ * Improve detecting empty files (#31332)
+ * Use `querySelector` over alternative DOM methods (#31280)
+ * Remove jQuery `.text()` (#30506)
+ * Use repo as of renderctx's member rather than a repoPath on metas (#29222)
+ * Refactor some frontend problems (#32646)
+ * Refactor DateUtils and merge TimeSince (#32409)
+ * Replace DateTime with proper functions (#32402)
+ * Replace DateTime with DateUtils (#32383)
+ * Convert frontend code to typescript (#31559)
+ * Refactor maven package registry (#33049) #33057
+ * Refactor testfixtures #33028
+
+* BUGFIXES
+ * Fix issues with inconsistent spacing in areas (#32607)
+ * Fix incomplete Actions status aggregations (#32859)
+ * In some lfs server implementations, they require the ref attribute. (#32838)
+ * Update the list of watchers and stargazers when clicking watch/unwatch or star/unstar (#32570)
+ * Fix `recentupdate` sorting bugs (#32505)
+ * Fix incorrect "Target branch does not exist" in PR title (#32222)
+ * Handle "close" actionable references for manual merges (#31879)
+ * render plain text file if the LFS object doesn't exist (#31812)
+ * Fix Null Pointer error for CommitStatusesHideActionsURL (#31731)
+ * Fix loadRepository error when access user dashboard (#31719)
+ * Hide the "Details" link of commit status when the user cannot access actions (#30156)
+ * Fix duplicate dropdown dividers (#32760)
+ * Fix SSPI button visibility when SSPI is the only enabled method (#32841)
+ * Fix overflow on org header (#32837)
+ * Exclude protected branches from recently pushed (#31748)
+ * Fix large image overflow in comment page (#31740)
+ * Fix milestone deadline and date related problems (#32339)
+ * Fix markdown preview $$ support (#31514)
+ * Fix a compilation error in the Gitpod environment (#32559)
+ * Fix PR diff review form submit (#32596)
+ * Fix a number of typescript issues (#32308)
+ * Fix some function names in comment (#32300)
+ * Fix absolute-date (#32375)
+ * Clarify Actions resources ownership (#31724)
+ * Try to fix ACME directory problem (#33072) #33077
+ * Inherit submodules from template repository content (#16237) #33068
+ * Use project's redirect url instead of composing url (#33058) #33064
+ * Fix toggle commit body button ui when latest commit message is long (#32997) #33034
+ * Fix package error handling and npm meta and empty repo guide #33112
+ * Fix empty git repo handling logic and fix mobile view (#33101) #33102
+ * Fix line-number and scroll bugs (#33094) #33095
+ * Fix bleve fuzziness search (#33078) #33087
+ * Fix broken forms #33082
+ * Fix empty repo updated time (#33120) #33124
+ * Add missing transaction when set merge #33113
+ * Fix issue comment number (#30556) #33055
+ * Fix duplicate co-author in squashed merge commit messages (#33020) #33054
+ * Fix Agit pull request permission check (#32999) #33005
+ * Fix scoped label ui when contains emoji (#33007) #33014
+ * Fix bug on activities (#33008) #33016
+ * Fix review code comment avatar alignment (#33031) #33032
+ * Fix templating in pull request comparison (#33025) #33038
+ * Fix bug automerge cannot be chosed when there is only 1 merge style (#33040) #33043
+ * Fix settings not being loaded at CLI (#26402) #33048
+ * Support for email addresses containing uppercase characters when activating user account (#32998) #33001
+ * Support org labels when adding labels by label names (#32988) #32996
+ * Do not render truncated links in markdown (#32980) #32983
+ * Demilestone should not include milestone (#32923) #32979
+ * Fix Azure blob object Seek (#32974) #32975
+ * Fix maven pom inheritance (#32943) #32976
+ * Fix textarea newline handle (#32966) #32977
+ * Fix outdated tmpl code (#32953) #32961
+ * Fix commit range paging (#32944) #32962
+ * Fix repo avatar conflict (#32958) #32960
+ * Fix trailing comma not matched in the case of alphanumeric issue (#32945)
+ * Relax the version checking for Arch packages (#32908) #32913
+ * Add more load functions to make sure the reference object loaded (#32901) #32912
+ * Filter reviews of one pull request in memory instead of database to reduce slow response because of lacking database index (#33106) #33128
+ * Fix git remote error check, fix dependencies, fix js error (#33129) #33133
+
+* MISC
+ * Optimize branch protection rule loading (#32280)
+ * Bump to go 1.23 (#31855)
+ * Remove unused call to $.HeadRepo in view_title template (#32317)
+ * Do not display `attestation-manifest` and use short sha256 instead of full sha256 (#32851)
+ * Upgrade htmx to 2.0.4 (#32834)
+ * Improve JSX/TSX support in code editor (#32833)
+ * Add User-Agent for gitea's self-implemented lfs client. (#32832)
+ * Use errors.New to replace fmt.Errorf with no parameters (#32800)
+ * Add "n commits" link to contributors in contributors graph page (#32799)
+ * Update dependencies, tweak eslint (#32719)
+ * Remove all "floated" CSS styles (#32691)
+ * Show tag name on branch/tag selector if repo shown from tag ref (#32689)
+ * Use new mail package instead of an unmintained one (#32682)
+ * Optimize the styling of icon buttons within file-header-right (#32675)
+ * Validate OAuth Redirect URIs (#32643)
+ * Support optional/configurable IAMEndpoint for Minio Client (#32581) (#32581)
+ * Make search box in issue sidebar dropdown list always show when scrolling (#32576)
+ * Bump CI,Flake and Snap to Node 22 (#32487)
+ * Update `github.com/meilisearch/meilisearch-go` (#32484)
+ * Add `DEFAULT_MIRROR_REPO_UNITS` and `DEFAULT_TEMPLATE_REPO_UNITS` options (#32416)
+ * Update go dependencies (#32389)
+ * Update JS and PY dependencies (#32388)
+ * Upgrade rollup to 4.24.0 (#32312)
+ * Upgrade vue to 3.5.12 (#32311)
+ * Improve the maintainblity of the reserved username list (#32229)
+ * Upgrade htmx to 2.0.3 (#32192)
+ * Count typescript files as frontend for labeling (#32088)
+ * Only use Host header from reverse proxy (#32060)
+ * Failed authentications are logged to level Warning (#32016)
+ * Enhance USER_DISABLED_FEATURES to allow disabling change username or full name (#31959)
+ * Distinguish official vs non-official reviews, add tool tips, and upgr… (#31924)
+ * Update mermaid to v11 (#31913)
+ * Bump relative-time-element to v4.4.3 (#31910)
+ * Upgrade `htmx` to `2.0.2` (#31847)
+ * Add warning message in merge instructions when `AutodetectManualMerge` was not enabled (#31805)
+ * Add types to various low-level functions (#31781)
+ * Update JS dependencies (#31766)
+ * Remove unused code from models/repos/release.go (#31756)
+ * Support delete user email in admin panel (#31690)
+ * Add `username` to OIDC introspection response (#31688)
+ * Use GetDisplayName() instead of DisplayName() to generate rss feeds (#31687)
+ * Code editor theme enhancements (#31629)
+ * Update JS dependencies (#31616)
+ * Add types for js globals (#31586)
+ * Add back esbuild-loader for .js files (#31585)
+ * Don't show hidden labels when filling out an issue template (#31576)
+ * Allow synchronizing user status from OAuth2 login providers (#31572)
+ * Display app name in the registration email title (#31562)
+ * Use stable version of fabric (#31526)
+ * Support legacy _links LFS batch responses (#31513)
+ * Fix JS error with disabled attachment and easymde (#31511)
+ * Always use HTML attributes for avatar size (#31509)
+ * Use nolyfill to remove some polyfills (#31468)
+ * Disable issue/PR comment button given empty input (#31463)
+ * Add simple JS init performance trace (#31459)
+ * Bump htmx to 2.0.0 (#31413)
+ * Update JS dependencies, remove `eslint-plugin-jquery` (#31402)
+ * Split org Propfile README to a new tab `overview` (#31373)
+ * Update nix flake and add gofumpt (#31320)
+ * Code optimization (#31315)
+ * Enable poetry non-package mode (#31282)
+ * Optimize profile layout to enhance visual experience (#31278)
+ * Update `golang.org/x/net` (#31260)
+ * Bump `@github/relative-time-element` to v4.4.1 (#31232)
+ * Remove unnecessary inline style for tab-size (#31224)
+ * Update golangci-lint to v1.59.0 (#31221)
+ * Update chroma to v2.14.0 (#31177)
+ * Update JS dependencies (#31120)
+ * Improve the handling of `jobs..if` (#31070)
+ * Clean up revive linter config, tweak golangci output (#30980)
+ * Use CSS `inset` shorthand (#30939)
+ * Forbid deprecated `break-word` in CSS (#30934)
+ * Remove obsolete monaco workaround (#30893)
+ * Update JS dependencies, add new eslint rules (#30840)
+ * Fix body margin shifting with modals, fix error on project column edit (#30831)
+ * Remove disk-clean workflow (#30741)
+ * Bump `github.com/google/go-github` to v61 (#30738)
+ * Add built js files to eslint ignore (#30737)
+ * Use `ProtonMail/go-crypto` for `opengpg` in tests (#30736)
+ * Upgrade xorm to v1.3.9 and improve some migrations Sync (#29899)
+ * Added default sorting milestones by name (#27084)
+ * Enable `unparam` linter (#31277)
+ * Use Alpine 3.21 for the docker images (#32924) #32951
+ * Bump x/net (#32896) #32899
+ * Use -s -w ldflags for release artifacts (#33041) #33042
+ * Remove aws go sdk package dependency (#33029) #33047
+
+## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12
+
+* SECURITY
+ * Fix misuse of PublicKeyCallback(#32810)
+* BUGFIXES
+ * Fix lfs migration (#32812) (#32818)
+ * Add missing two sync feed for refs/pull (#32815)
+* TESTING
+ * Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
+
+## [1.22.5](https://github.com/go-gitea/gitea/releases/tag/v1.22.5) - 2024-12-11
+
+* SECURITY
+ * Upgrade crypto library (#32791)
+ * Fix delete branch perm checking (#32654) (#32707)
+* BUGFIXES
+ * Add standard-compliant route to serve outdated R packages (#32783) (#32789)
+ * Fix internal server error when updating labels without write permission (#32776) (#32785)
+ * Add Swift login endpoint (#32693) (#32701)
+ * Fix fork page branch selection (#32711) (#32725)
+ * Fix word overflow in file search page (#32695) (#32699)
+ * Fix gogit `GetRefCommitID` (#32705) (#32712)
+ * Fix race condition in mermaid observer (#32599) (#32673)
+ * Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
+ * Bump relative-time-element to v4.4.4 (#32739)
+* PERFORMANCE
+ * Make wiki pages visit fast (#32732) (#32745)
+* MISC
+ * Don't create action when syncing mirror pull refs (#32659) (#32664)
+
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/v1.22.4) - 2024-11-14
* SECURITY
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 60146276db..11c99d1e3a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -182,7 +182,7 @@ Here's how to run the test suite:
## Translation
-All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
+All translation work happens on [Crowdin](https://translate.gitea.com).
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
It is synced regularly with Crowdin. \
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
diff --git a/Dockerfile b/Dockerfile
index 2a6b1dd6b7..c9e6a2d3db 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
+FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.20
+FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000
@@ -78,7 +78,7 @@ ENV GITEA_CUSTOM=/data/gitea
VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/entrypoint"]
-CMD ["/bin/s6-svscan", "/etc/s6"]
+CMD ["/usr/bin/s6-svscan", "/etc/s6"]
COPY --from=build-env /tmp/local /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index 26f02205a7..558e6cf73b 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
+FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.20
+FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000
@@ -52,6 +52,7 @@ RUN apk --no-cache add \
git \
curl \
gnupg \
+ openssh-keygen \
&& rm -rf /var/cache/apk/*
RUN addgroup \
diff --git a/MAINTAINERS b/MAINTAINERS
index ad02ecc755..7643ab000f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -31,7 +31,6 @@ Gary Kim (@gary-kim)
Guillermo Prandi (@guillep2k)
Mura Li (@typeless)
6543 <6543@obermui.de> (@6543)
-jaqra (@jaqra)
David Svantesson (@davidsvantesson)
a1012112796 <1012112796@qq.com> (@a1012112796)
Karl Heinz Marbaise (@khmarbaise)
@@ -63,3 +62,6 @@ Yu Liu <1240335630@qq.com> (@HEREYUA)
Kemal Zebari (@kemzeb)
Rowan Bohde (@bohde)
hiifong (@hiifong)
+metiftikci (@metiftikci)
+Christopher Homberger (@ChristopherHX)
+Tobias Balle-Petersen (@tobiasbp)
diff --git a/Makefile b/Makefile
index 4889958c3b..6a3fa60e49 100644
--- a/Makefile
+++ b/Makefile
@@ -23,20 +23,21 @@ SHASUM ?= shasum -a 256
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
COMMA := ,
-XGO_VERSION := go-1.23.x
+XGO_VERSION := go-1.24.x
AIR_PACKAGE ?= github.com/air-verse/air@v1
-EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
-GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
-GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
-GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
-MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
-SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
+EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
+GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6
+GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
+MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
+SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.32.3
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
-GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.15.3
+GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.1
+GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@@ -73,13 +74,13 @@ EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
MAKE_EVIDENCE_DIR := .make_evidence
+GOTESTFLAGS ?=
ifeq ($(RACE_ENABLED),true)
GOFLAGS += -race
GOTESTFLAGS += -race
endif
STORED_VERSION_FILE := VERSION
-HUGO_VERSION ?= 0.111.3
GITHUB_REF_TYPE ?= branch
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
@@ -109,20 +110,17 @@ endif
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
-LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
+LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
-FOMANTIC_WORK_DIR := web_src/fomantic
-
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
-BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
-BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
+BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@@ -139,25 +137,19 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
-TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
+TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
GO_DIRS := build cmd models modules routers services tests
WEB_DIRS := web_src/js web_src/css
-ESLINT_FILES := web_src/js tools *.js *.ts tests/e2e
+ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
-SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
+SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
GO_SOURCES := $(wildcard *.go)
-GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
+GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
GO_SOURCES += $(GENERATED_GO_DEST)
-GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
-
-ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
- GO_SOURCES += $(BINDATA_DEST)
- GENERATED_GO_DEST += $(BINDATA_DEST)
-endif
# Force installation of playwright dependencies by setting this flag
ifdef DEPS_PLAYWRIGHT
@@ -165,10 +157,8 @@ ifdef DEPS_PLAYWRIGHT
endif
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
-SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g
-SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g
+SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
SWAGGER_EXCLUDE := code.gitea.io/sdk
-SWAGGER_NEWLINE_COMMAND := -e '$$a\'
TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea
@@ -189,67 +179,11 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
all: build
.PHONY: help
-help:
- @echo "Make Routines:"
- @echo " - \"\" equivalent to \"build\""
- @echo " - build build everything"
- @echo " - frontend build frontend files"
- @echo " - backend build backend files"
- @echo " - watch watch everything and continuously rebuild"
- @echo " - watch-frontend watch frontend files and continuously rebuild"
- @echo " - watch-backend watch backend files and continuously rebuild"
- @echo " - clean delete backend and integration files"
- @echo " - clean-all delete backend, frontend and integration files"
- @echo " - deps install dependencies"
- @echo " - deps-frontend install frontend dependencies"
- @echo " - deps-backend install backend dependencies"
- @echo " - deps-tools install tool dependencies"
- @echo " - deps-py install python dependencies"
- @echo " - lint lint everything"
- @echo " - lint-fix lint everything and fix issues"
- @echo " - lint-actions lint action workflow files"
- @echo " - lint-frontend lint frontend files"
- @echo " - lint-frontend-fix lint frontend files and fix issues"
- @echo " - lint-backend lint backend files"
- @echo " - lint-backend-fix lint backend files and fix issues"
- @echo " - lint-go lint go files"
- @echo " - lint-go-fix lint go files and fix issues"
- @echo " - lint-go-vet lint go files with vet"
- @echo " - lint-go-gopls lint go files with gopls"
- @echo " - lint-js lint js files"
- @echo " - lint-js-fix lint js files and fix issues"
- @echo " - lint-css lint css files"
- @echo " - lint-css-fix lint css files and fix issues"
- @echo " - lint-md lint markdown files"
- @echo " - lint-swagger lint swagger files"
- @echo " - lint-templates lint template files"
- @echo " - lint-yaml lint yaml files"
- @echo " - lint-spell lint spelling"
- @echo " - lint-spell-fix lint spelling and fix issues"
- @echo " - checks run various consistency checks"
- @echo " - checks-frontend check frontend files"
- @echo " - checks-backend check backend files"
- @echo " - test test everything"
- @echo " - test-frontend test frontend files"
- @echo " - test-backend test backend files"
- @echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
- @echo " - update update js and py dependencies"
- @echo " - update-js update js dependencies"
- @echo " - update-py update py dependencies"
- @echo " - webpack build webpack files"
- @echo " - svg build svg files"
- @echo " - fomantic build fomantic files"
- @echo " - generate run \"go generate\""
- @echo " - fmt format the Go code"
- @echo " - generate-license update license files"
- @echo " - generate-gitignore update gitignore files"
- @echo " - generate-manpage generate manpage"
- @echo " - generate-swagger generate the swagger spec from code comments"
- @echo " - swagger-validate check if the swagger spec is valid"
- @echo " - go-licenses regenerate go licenses"
- @echo " - tidy run go mod tidy"
- @echo " - test[\#TestSpecificName] run unit test"
- @echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
+help: Makefile ## print Makefile help information.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
+ @printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright"
+ @printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
+ @printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
.PHONY: go-check
go-check:
@@ -280,12 +214,12 @@ node-check:
fi
.PHONY: clean-all
-clean-all: clean
+clean-all: clean ## delete backend, frontend and integration files
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean
-clean:
- rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
+clean: ## delete backend and integration files
+ rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST_WILDCARD) \
integrations*.test \
e2e*.test \
tests/integration/gitea-integration-* \
@@ -296,7 +230,7 @@ clean:
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
.PHONY: fmt
-fmt:
+fmt: ## format the Go and template code
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
@@ -311,7 +245,20 @@ fmt-check: fmt
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
+ exit 1; \
+ fi
+
+.PHONY: fix
+fix: ## apply automated fixes to Go code
+ $(GO) run $(GOPLS_MODERNIZE_PACKAGE) -fix ./...
+
+.PHONY: fix-check
+fix-check: fix
+ @diff=$$(git diff --color=always $(GO_SOURCES)); \
+ if [ -n "$$diff" ]; then \
+ echo "Please run 'make fix' and commit the result:"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -325,95 +272,95 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
endif
.PHONY: generate-swagger
-generate-swagger: $(SWAGGER_SPEC)
+generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
-$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
- $(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
- $(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
+$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
+ $(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
.PHONY: swagger-check
swagger-check: generate-swagger
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
if [ -n "$$diff" ]; then \
echo "Please run 'make generate-swagger' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: swagger-validate
-swagger-validate:
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
+swagger-validate: ## check if the swagger spec is valid
+ @# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
+ @$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
+ @# FIXME: there are some warnings
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
+ @$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
.PHONY: checks
-checks: checks-frontend checks-backend
+checks: checks-frontend checks-backend ## run various consistency checks
.PHONY: checks-frontend
-checks-frontend: lockfile-check svg-check
+checks-frontend: lockfile-check svg-check ## check frontend files
.PHONY: checks-backend
-checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check
+checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files
.PHONY: lint
-lint: lint-frontend lint-backend lint-spell
+lint: lint-frontend lint-backend lint-spell ## lint everything
.PHONY: lint-fix
-lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix
+lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
.PHONY: lint-frontend
-lint-frontend: lint-js lint-css
+lint-frontend: lint-js lint-css ## lint frontend files
.PHONY: lint-frontend-fix
-lint-frontend-fix: lint-js-fix lint-css-fix
+lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
.PHONY: lint-backend
-lint-backend: lint-go lint-go-vet lint-go-gopls lint-editorconfig
+lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
.PHONY: lint-backend-fix
-lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
+lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
.PHONY: lint-js
-lint-js: node_modules
+lint-js: node_modules ## lint js files
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
npx vue-tsc
.PHONY: lint-js-fix
-lint-js-fix: node_modules
+lint-js-fix: node_modules ## lint js files and fix issues
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
npx vue-tsc
.PHONY: lint-css
-lint-css: node_modules
+lint-css: node_modules ## lint css files
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
.PHONY: lint-css-fix
-lint-css-fix: node_modules
+lint-css-fix: node_modules ## lint css files and fix issues
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
.PHONY: lint-swagger
-lint-swagger: node_modules
+lint-swagger: node_modules ## lint swagger files
npx spectral lint -q -F hint $(SWAGGER_SPEC)
.PHONY: lint-md
-lint-md: node_modules
+lint-md: node_modules ## lint markdown files
npx markdownlint *.md
.PHONY: lint-spell
-lint-spell:
+lint-spell: ## lint spelling
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
.PHONY: lint-spell-fix
-lint-spell-fix:
+lint-spell-fix: ## lint spelling and fix issues
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
.PHONY: lint-go
-lint-go:
+lint-go: ## lint go files
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint-go-fix
-lint-go-fix:
+lint-go-fix: ## lint go files and fix issues
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
# workaround step for the lint-go-windows CI task because 'go run' can not
@@ -423,57 +370,58 @@ lint-go-windows:
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
golangci-lint run
-.PHONY: lint-go-vet
-lint-go-vet:
- @echo "Running go vet..."
+.PHONY: lint-go-gitea-vet
+lint-go-gitea-vet: ## lint go files with gitea-vet
+ @echo "Running gitea-vet..."
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
@$(GO) vet -vettool=gitea-vet ./...
.PHONY: lint-go-gopls
-lint-go-gopls:
+lint-go-gopls: ## lint go files with gopls
@echo "Running gopls check..."
- @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
+ @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
.PHONY: lint-editorconfig
lint-editorconfig:
+ @echo "Running editorconfig check..."
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
.PHONY: lint-actions
-lint-actions:
+lint-actions: ## lint action workflow files
$(GO) run $(ACTIONLINT_PACKAGE)
.PHONY: lint-templates
-lint-templates: .venv node_modules
+lint-templates: .venv node_modules ## lint template files
@node tools/lint-templates-svg.js
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
.PHONY: lint-yaml
-lint-yaml: .venv
- @poetry run yamllint .
+lint-yaml: .venv ## lint yaml files
+ @poetry run yamllint -s .
.PHONY: watch
-watch:
+watch: ## watch everything and continuously rebuild
@bash tools/watch.sh
.PHONY: watch-frontend
-watch-frontend: node-check node_modules
+watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
@rm -rf $(WEBPACK_DEST_ENTRIES)
NODE_ENV=development npx webpack --watch --progress
.PHONY: watch-backend
-watch-backend: go-check
+watch-backend: go-check ## watch backend files and continuously rebuild
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
.PHONY: test
-test: test-frontend test-backend
+test: test-frontend test-backend ## test everything
.PHONY: test-backend
-test-backend:
+test-backend: ## test backend files
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
.PHONY: test-frontend
-test-frontend: node_modules
+test-frontend: node_modules ## test frontend files
npx vitest
.PHONY: test-check
@@ -482,7 +430,7 @@ test-check:
@diff=$$(git status -s); \
if [ -n "$$diff" ]; then \
echo "make test-backend has changed files in the source tree:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
echo "You should change the tests to create these files in a temporary directory."; \
echo "Do not simply add these files to .gitignore"; \
exit 1; \
@@ -505,7 +453,7 @@ unit-test-coverage:
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: tidy
-tidy:
+tidy: ## run go mod tidy
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
@@ -519,15 +467,17 @@ tidy-check: tidy
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make tidy' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: go-licenses
-go-licenses: $(GO_LICENSE_FILE)
+go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
$(GO_LICENSE_FILE): go.mod go.sum
- -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
+ @rm -rf $(GO_LICENSE_FILE)
+ $(GO) install $(GO_LICENSES_PACKAGE)
+ -GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
@rm -rf $(GO_LICENSE_TMP_DIR)
@@ -771,17 +721,17 @@ install: $(wildcard *.go)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
-build: frontend backend
+build: frontend backend ## build everything
.PHONY: frontend
-frontend: $(WEBPACK_DEST)
+frontend: $(WEBPACK_DEST) ## build frontend files
.PHONY: backend
-backend: go-check generate-backend $(EXECUTABLE)
+backend: go-check generate-backend $(EXECUTABLE) ## build backend files
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
.PHONY: generate
-generate: generate-backend
+generate: generate-backend ## run "go generate"
.PHONY: generate-backend
generate-backend: $(TAGS_PREREQ) generate-go
@@ -793,7 +743,7 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check
security-check:
- go run $(GOVULNCHECK_PACKAGE) ./...
+ go run $(GOVULNCHECK_PACKAGE) -show color ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
@@ -806,22 +756,22 @@ $(DIST_DIRS):
.PHONY: release-windows
release-windows: | $(DIST_DIRS)
- CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
+ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq (,$(findstring gogit,$(TAGS)))
- CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
+ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
endif
.PHONY: release-linux
release-linux: | $(DIST_DIRS)
- CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
+ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
.PHONY: release-darwin
release-darwin: | $(DIST_DIRS)
- CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
+ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
.PHONY: release-freebsd
release-freebsd: | $(DIST_DIRS)
- CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
+ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
.PHONY: release-copy
release-copy: | $(DIST_DIRS)
@@ -846,20 +796,20 @@ release-sources: | $(DIST_DIRS)
rm -f $(STORED_VERSION_FILE)
.PHONY: deps
-deps: deps-frontend deps-backend deps-tools deps-py
+deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies
.PHONY: deps-py
-deps-py: .venv
+deps-py: .venv ## install python dependencies
.PHONY: deps-frontend
-deps-frontend: node_modules
+deps-frontend: node_modules ## install frontend dependencies
.PHONY: deps-backend
-deps-backend:
+deps-backend: ## install backend dependencies
$(GO) mod download
.PHONY: deps-tools
-deps-tools:
+deps-tools: ## install tool dependencies
$(GO) install $(AIR_PACKAGE) & \
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) install $(GOFUMPT_PACKAGE) & \
@@ -872,6 +822,7 @@ deps-tools:
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
+ $(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
wait
node_modules: package-lock.json
@@ -883,10 +834,10 @@ node_modules: package-lock.json
@touch .venv
.PHONY: update
-update: update-js update-py
+update: update-js update-py ## update js and py dependencies
.PHONY: update-js
-update-js: node-check | node_modules
+update-js: node-check | node_modules ## update js dependencies
npx updates -u -f package.json
rm -rf node_modules package-lock.json
npm install --package-lock
@@ -895,27 +846,14 @@ update-js: node-check | node_modules
@touch node_modules
.PHONY: update-py
-update-py: node-check | node_modules
+update-py: node-check | node_modules ## update py dependencies
npx updates -u -f pyproject.toml
rm -rf .venv poetry.lock
poetry install
@touch .venv
-.PHONY: fomantic
-fomantic:
- rm -rf $(FOMANTIC_WORK_DIR)/build
- cd $(FOMANTIC_WORK_DIR) && npm install --no-save
- cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
- cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
- $(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
- cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
- # fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
- $(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
- $(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
- rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
-
.PHONY: webpack
-webpack: $(WEBPACK_DEST)
+webpack: $(WEBPACK_DEST) ## build webpack files
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules
@@ -925,7 +863,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@touch $(WEBPACK_DEST)
.PHONY: svg
-svg: node-check | node_modules
+svg: node-check | node_modules ## build svg files
rm -rf $(SVG_DEST_DIR)
node tools/generate-svg.js
@@ -935,7 +873,7 @@ svg-check: svg
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -946,7 +884,7 @@ lockfile-check:
if [ -n "$$diff" ]; then \
echo "package-lock.json is inconsistent with package.json"; \
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -960,12 +898,8 @@ update-translations:
mv ./translations/*.ini ./options/locale/
rmdir ./translations
-.PHONY: generate-license
-generate-license:
- $(GO) run build/generate-licenses.go
-
.PHONY: generate-gitignore
-generate-gitignore:
+generate-gitignore: ## update gitignore files
$(GO) run build/generate-gitignores.go
.PHONY: generate-images
@@ -974,7 +908,7 @@ generate-images: | node_modules
node tools/generate-images.js $(TAGS)
.PHONY: generate-manpage
-generate-manpage:
+generate-manpage: ## generate manpage
@[ -f gitea ] || make backend
@mkdir -p man/man1/ man/man5
@./gitea docs --man > man/man1/gitea.1
diff --git a/README.md b/README.md
index c280c832ac..017ca629d0 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,9 @@
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[](https://opensource.org/licenses/MIT "License: MIT")
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
-[](https://crowdin.com/project/gitea "Crowdin")
+[](https://translate.gitea.com "Crowdin")
-[View this document in Chinese](./README_ZH.md)
+[ç¹é«”䏿–‡](./README.zh-tw.md) | [ç®€ä½“ä¸æ–‡](./README.zh-cn.md)
## Purpose
@@ -31,6 +31,14 @@ For accessing free Gitea service (with a limited number of repositories), you ca
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
+## Documentation
+
+You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
+
+It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
+
+If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
+
## Building
From the root of the source tree, run:
@@ -52,6 +60,8 @@ More info: https://docs.gitea.com/installation/install-from-source
## Using
+After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
+
./gitea web
> [!NOTE]
@@ -68,22 +78,25 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
## Translating
-Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
+[](https://translate.gitea.com)
+
+Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
-https://docs.gitea.com/contributing/localization
+Get more information from [documentation](https://docs.gitea.com/contributing/localization).
-[](https://crowdin.com/project/gitea)
+## Official and Third-Party Projects
-## Further information
+We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
-For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
-If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
+We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
-We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
+## Communication
-The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
## Authors
@@ -122,18 +135,79 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
+**Where can I find the security patches?**
+
+In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
+
## License
This project is licensed under the MIT License.
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
for the full license text.
-## Screenshots
+## Further information
-Looking for an overview of the interface? Check it out!
+
+Looking for an overview of the interface? Check it out!
-||||
-|:---:|:---:|:---:|
-||||
-||||
-||||
+### Login/Register Page
+
+
+
+
+### User Dashboard
+
+
+
+
+
+
+### User Profile
+
+
+
+### Explore
+
+
+
+
+
+### Repository
+
+
+
+
+
+
+
+
+
+#### Repository Issue
+
+
+
+
+#### Repository Pull Requests
+
+
+
+
+
+
+#### Repository Actions
+
+
+
+
+#### Repository Activity
+
+
+
+
+
+
+### Organization
+
+
+
+
diff --git a/README.zh-cn.md b/README.zh-cn.md
new file mode 100644
index 0000000000..f34b25b945
--- /dev/null
+++ b/README.zh-cn.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[](https://opensource.org/licenses/MIT "License: MIT")
+[](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [ç¹é«”䏿–‡](./README.zh-tw.md)
+
+## 目的
+
+è¿™ä¸ªé¡¹ç›®çš„ç›®æ ‡æ˜¯æä¾›æœ€ç®€å•ã€æœ€å¿«é€Ÿã€æœ€æ— ç—›çš„æ–¹å¼æ¥è®¾ç½®è‡ªæ‰˜ç®¡çš„ Git æœåŠ¡ã€‚
+
+由于 Gitea 是用 Go è¯è¨€ç¼–写的,它å¯ä»¥åœ¨ Go 支æŒçš„æ‰€æœ‰å¹³å°å’Œæž¶æž„上è¿è¡Œï¼ŒåŒ…括 Linuxã€macOS å’Œ Windows çš„ x86ã€amd64ã€ARM å’Œ PowerPC 架构。这个项目自 2016 å¹´ 11 月从 [Gogs](https://gogs.io) [分å‰](https://blog.gitea.com/welcome-to-gitea/) 而æ¥ï¼Œä½†å·²ç»æœ‰äº†å¾ˆå¤šå˜åŒ–。
+
+在线演示å¯ä»¥è®¿é—® [demo.gitea.com](https://demo.gitea.com)。
+
+è¦è®¿é—®å…费的 Gitea æœåŠ¡ï¼ˆæœ‰ä¸€å®šæ•°é‡çš„仓库é™åˆ¶ï¼‰ï¼Œå¯ä»¥è®¿é—® [gitea.com](https://gitea.com/user/login)。
+
+è¦å¿«é€Ÿéƒ¨ç½²æ‚¨è‡ªå·±çš„专用 Gitea 实例,å¯ä»¥åœ¨ [cloud.gitea.com](https://cloud.gitea.com) 开始å…费试用。
+
+## 文件
+
+您å¯ä»¥åœ¨æˆ‘们的官方 [文件网站](https://docs.gitea.com/) 上找到全é¢çš„æ–‡ä»¶ã€‚
+
+它包括安装ã€ç®¡ç†ã€ä½¿ç”¨ã€å¼€å‘ã€è´¡çŒ®æŒ‡å—ç‰ï¼Œå¸®åŠ©æ‚¨å¿«é€Ÿå…¥é—¨å¹¶æœ‰æ•ˆåœ°æŽ¢ç´¢æ‰€æœ‰åŠŸèƒ½ã€‚
+
+如果您有任何建议或想è¦è´¡çŒ®ï¼Œå¯ä»¥è®¿é—® [文件仓库](https://gitea.com/gitea/docs)
+
+## 构建
+
+从æºä»£ç æ ‘çš„æ ¹ç›®å½•è¿è¡Œï¼š
+
+ TAGS="bindata" make build
+
+å¦‚æžœéœ€è¦ SQLite 支æŒï¼š
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` ç›®æ ‡åˆ†ä¸ºä¸¤ä¸ªåç›®æ ‡ï¼š
+
+- `make backend` éœ€è¦ [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) ä¸å®šä¹‰ã€‚
+- `make frontend` éœ€è¦ [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需è¦äº’è”网连接æ¥ä¸‹è½½ go å’Œ npm 模å—。从包å«é¢„构建å‰ç«¯æ–‡ä»¶çš„官方æºä»£ç 压缩包构建时,ä¸ä¼šè§¦å‘ `frontend` ç›®æ ‡ï¼Œå› æ¤å¯ä»¥åœ¨æ²¡æœ‰ Node.js 的情况下构建。
+
+更多信æ¯ï¼šhttps://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+构建åŽï¼Œé»˜è®¤æƒ…况下会在æºä»£ç æ ‘çš„æ ¹ç›®å½•ç”Ÿæˆä¸€ä¸ªå为 `gitea` 的二进制文件。è¦è¿è¡Œå®ƒï¼Œè¯·ä½¿ç”¨ï¼š
+
+ ./gitea web
+
+> [!注æ„]
+> 如果您对使用我们的 API 感兴趣,我们æä¾›äº†å®žéªŒæ€§æ”¯æŒï¼Œå¹¶é™„有 [文件](https://docs.gitea.com/api)。
+
+## 贡献
+
+预期的工作æµç¨‹æ˜¯ï¼šFork -> Patch -> Push -> Pull Request
+
+> [!注æ„]
+>
+> 1. **在开始进行 Pull Request 之å‰ï¼Œæ‚¨å¿…须阅读 [贡献者指å—](CONTRIBUTING.md)。**
+> 2. 如果您在项目ä¸å‘çŽ°äº†æ¼æ´žï¼Œè¯·ç§ä¸‹å†™ä¿¡ç»™ **security@gitea.io**。谢谢ï¼
+
+## 翻译
+
+[](https://translate.gitea.com)
+
+翻译通过 [Crowdin](https://translate.gitea.com) è¿›è¡Œã€‚å¦‚æžœæ‚¨æƒ³ç¿»è¯‘æˆæ–°çš„è¯è¨€ï¼Œè¯·åœ¨ Crowdin 项目ä¸è¯·æ±‚管ç†å‘˜æ·»åŠ æ–°è¯è¨€ã€‚
+
+您也å¯ä»¥åˆ›å»ºä¸€ä¸ª issue æ¥æ·»åŠ è¯è¨€ï¼Œæˆ–者在 discord çš„ #translation 频é“上询问。如果您需è¦ä¸Šä¸‹æ–‡æˆ–å‘现一些翻译问题,å¯ä»¥åœ¨å—符串上留言或在 Discord ä¸Šè¯¢é—®ã€‚å¯¹äºŽä¸€èˆ¬çš„ç¿»è¯‘é—®é¢˜ï¼Œæ–‡æ¡£ä¸æœ‰ä¸€ä¸ªéƒ¨åˆ†ã€‚ç›®å‰æœ‰ç‚¹ç©ºï¼Œä½†æˆ‘们希望éšç€é—®é¢˜çš„出现而填充它。
+
+更多信æ¯è¯·å‚阅 [文件](https://docs.gitea.com/contributing/localization)。
+
+## 官方和第三方项目
+
+我们æä¾›äº†ä¸€ä¸ªå®˜æ–¹çš„ [go-sdk](https://gitea.com/gitea/go-sdk),一个å为 [tea](https://gitea.com/gitea/tea) çš„ CLI 工具和一个 Gitea Action çš„ [action runner](https://gitea.com/gitea/act_runner)。
+
+我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您å¯ä»¥åœ¨é‚£é‡Œå‘现更多的第三方项目,包括 SDKã€æ’ä»¶ã€ä¸»é¢˜ç‰ã€‚
+
+## 通讯
+
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵盖的问题,å¯ä»¥åœ¨æˆ‘们的 [Discord æœåС噍](https://discord.gg/Gitea) 上与我们è”系,或者在 [discourse 论å›](https://forum.gitea.com/) 上创建帖å。
+
+## 作者
+
+- [维护者](https://github.com/orgs/go-gitea/people)
+- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
+- [翻译者](options/locale/TRANSLATORS)
+
+## 支æŒè€…
+
+感谢所有支æŒè€…ï¼ ðŸ™ [[æˆä¸ºæ”¯æŒè€…](https://opencollective.com/gitea#backer)]
+
+
+
+## 赞助商
+
+通过æˆä¸ºèµžåŠ©å•†æ¥æ”¯æŒè¿™ä¸ªé¡¹ç›®ã€‚æ‚¨çš„æ ‡å¿—å°†æ˜¾ç¤ºåœ¨è¿™é‡Œï¼Œå¹¶å¸¦æœ‰é“¾æŽ¥åˆ°æ‚¨çš„ç½‘ç«™ã€‚ [[æˆä¸ºèµžåЩ商](https://opencollective.com/gitea#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+## 常è§é—®é¢˜
+
+**Gitea 怎么å‘音?**
+
+Gitea çš„å‘音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY)ï¼Œå°±åƒ "gi-tea" ä¸€æ ·ï¼Œg 是硬音。
+
+**为什么这个项目没有托管在 Gitea 实例上?**
+
+我们æ£åœ¨ [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪里å¯ä»¥æ‰¾åˆ°å®‰å…¨è¡¥ä¸ï¼Ÿ**
+
+在 [å‘布日志](https://github.com/go-gitea/gitea/releases) 或 [å˜æ›´æ—¥å¿—](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) ä¸ï¼Œæœç´¢å…³é”®è¯ `SECURITY` 以找到安全补ä¸ã€‚
+
+## 许å¯è¯
+
+è¿™ä¸ªé¡¹ç›®æ˜¯æ ¹æ® MIT 许å¯è¯æŽˆæƒçš„。
+请å‚阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获å–完整的许å¯è¯æ–‡æœ¬ã€‚
+
+## 进一æ¥ä¿¡æ¯
+
+
+å¯»æ‰¾ç•Œé¢æ¦‚述?查看这里ï¼
+
+### 登录/注册页é¢
+
+
+
+
+### 用户仪表æ¿
+
+
+
+
+
+
+### 用户资料
+
+
+
+### 探索
+
+
+
+
+
+### 仓库
+
+
+
+
+
+
+
+
+
+#### 仓库问题
+
+
+
+
+#### 仓库拉å–请求
+
+
+
+
+
+
+#### 仓库æ“作
+
+
+
+
+#### 仓库活动
+
+
+
+
+
+
+### 组织
+
+
+
+
diff --git a/README.zh-tw.md b/README.zh-tw.md
new file mode 100644
index 0000000000..9de3f85dd5
--- /dev/null
+++ b/README.zh-tw.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[](https://opensource.org/licenses/MIT "License: MIT")
+[](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [ç®€ä½“ä¸æ–‡](./README.zh-cn.md)
+
+## 目的
+
+é€™å€‹é …ç›®çš„ç›®æ¨™æ˜¯æä¾›æœ€ç°¡å–®ã€æœ€å¿«é€Ÿã€æœ€ç„¡ç—›çš„æ–¹å¼ä¾†è¨ç½®è‡ªè¨—管的 Git æœå‹™ã€‚
+
+由於 Gitea 是用 Go 語言編寫的,它å¯ä»¥åœ¨ Go 支æ´çš„æ‰€æœ‰å¹³å°å’Œæž¶æ§‹ä¸Šé‹è¡Œï¼ŒåŒ…括 Linuxã€macOS å’Œ Windows çš„ x86ã€amd64ã€ARM å’Œ PowerPC æž¶æ§‹ã€‚é€™å€‹é …ç›®è‡ª 2016 å¹´ 11 月從 [Gogs](https://gogs.io) [分å‰](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
+
+在線演示å¯ä»¥è¨ªå• [demo.gitea.com](https://demo.gitea.com)。
+
+è¦è¨ªå•å…費的 Gitea æœå‹™ï¼ˆæœ‰ä¸€å®šæ•¸é‡çš„倉庫é™åˆ¶ï¼‰ï¼Œå¯ä»¥è¨ªå• [gitea.com](https://gitea.com/user/login)。
+
+è¦å¿«é€Ÿéƒ¨ç½²æ‚¨è‡ªå·±çš„專用 Gitea 實例,å¯ä»¥åœ¨ [cloud.gitea.com](https://cloud.gitea.com) é–‹å§‹å…費試用。
+
+## 文件
+
+您å¯ä»¥åœ¨æˆ‘們的官方 [文件網站](https://docs.gitea.com/) 上找到全é¢çš„æ–‡ä»¶ã€‚
+
+它包括安è£ã€ç®¡ç†ã€ä½¿ç”¨ã€é–‹ç™¼ã€è²¢ç»æŒ‡å—ç‰ï¼Œå¹«åŠ©æ‚¨å¿«é€Ÿå…¥é–€ä¸¦æœ‰æ•ˆåœ°æŽ¢ç´¢æ‰€æœ‰åŠŸèƒ½ã€‚
+
+å¦‚æžœæ‚¨æœ‰ä»»ä½•å»ºè°æˆ–想è¦è²¢ç»ï¼Œå¯ä»¥è¨ªå• [文件倉庫](https://gitea.com/gitea/docs)
+
+## 構建
+
+從æºä»£ç¢¼æ¨¹çš„æ ¹ç›®éŒ„é‹è¡Œï¼š
+
+ TAGS="bindata" make build
+
+å¦‚æžœéœ€è¦ SQLite 支æ´ï¼š
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` 目標分為兩個å目標:
+
+- `make backend` éœ€è¦ [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) ä¸å®šç¾©ã€‚
+- `make frontend` éœ€è¦ [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需è¦äº’è¯ç¶²é€£æŽ¥ä¾†ä¸‹è¼‰ go å’Œ npm 模塊。從包å«é 構建å‰ç«¯æ–‡ä»¶çš„官方æºä»£ç¢¼å£“ç¸®åŒ…æ§‹å»ºæ™‚ï¼Œä¸æœƒè§¸ç™¼ `frontend` ç›®æ¨™ï¼Œå› æ¤å¯ä»¥åœ¨æ²’有 Node.js 的情æ³ä¸‹æ§‹å»ºã€‚
+
+更多信æ¯ï¼šhttps://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+æ§‹å»ºå¾Œï¼Œé»˜èªæƒ…æ³ä¸‹æœƒåœ¨æºä»£ç¢¼æ¨¹çš„æ ¹ç›®éŒ„生æˆä¸€å€‹å為 `gitea` 的二進制文件。è¦é‹è¡Œå®ƒï¼Œè«‹ä½¿ç”¨ï¼š
+
+ ./gitea web
+
+> [!注æ„]
+> 如果您å°ä½¿ç”¨æˆ‘們的 API 感興趣,我們æä¾›äº†å¯¦é©—性支æ´ï¼Œä¸¦é™„有 [文件](https://docs.gitea.com/api)。
+
+## è²¢ç»
+
+é æœŸçš„工作æµç¨‹æ˜¯ï¼šFork -> Patch -> Push -> Pull Request
+
+> [!注æ„]
+>
+> 1. **在開始進行 Pull Request 之å‰ï¼Œæ‚¨å¿…é ˆé–±è®€ [è²¢ç»è€…指å—](CONTRIBUTING.md)。**
+> 2. å¦‚æžœæ‚¨åœ¨é …ç›®ä¸ç™¼ç¾äº†æ¼æ´žï¼Œè«‹ç§ä¸‹å¯«ä¿¡çµ¦ **security@gitea.io**。è¬è¬ï¼
+
+## ç¿»è¯
+
+[](https://translate.gitea.com)
+
+ç¿»è¯é€šéŽ [Crowdin](https://translate.gitea.com) é€²è¡Œã€‚å¦‚æžœæ‚¨æƒ³ç¿»è¯æˆæ–°çš„語言,請在 Crowdin é …ç›®ä¸è«‹æ±‚管ç†å“¡æ·»åŠ æ–°èªžè¨€ã€‚
+
+您也å¯ä»¥å‰µå»ºä¸€å€‹ issue ä¾†æ·»åŠ èªžè¨€ï¼Œæˆ–è€…åœ¨ discord çš„ #translation é »é“上詢å•。如果您需è¦ä¸Šä¸‹æ–‡æˆ–發ç¾ä¸€äº›ç¿»è¯å•題,å¯ä»¥åœ¨å—符串上留言或在 Discord 上詢å•ã€‚å°æ–¼ä¸€èˆ¬çš„ç¿»è¯å•é¡Œï¼Œæ–‡æª”ä¸æœ‰ä¸€å€‹éƒ¨åˆ†ã€‚ç›®å‰æœ‰é»žç©ºï¼Œä½†æˆ‘們希望隨著å•題的出ç¾è€Œå¡«å……它。
+
+更多信æ¯è«‹åƒé–± [文件](https://docs.gitea.com/contributing/localization)。
+
+## å®˜æ–¹å’Œç¬¬ä¸‰æ–¹é …ç›®
+
+我們æä¾›äº†ä¸€å€‹å®˜æ–¹çš„ [go-sdk](https://gitea.com/gitea/go-sdk),一個å為 [tea](https://gitea.com/gitea/tea) çš„ CLI 工具和一個 Gitea Action çš„ [action runner](https://gitea.com/gitea/act_runner)。
+
+我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) ç¶è·äº†ä¸€å€‹ Gitea ç›¸é—œé …ç›®çš„åˆ—è¡¨ï¼Œæ‚¨å¯ä»¥åœ¨é‚£è£¡ç™¼ç¾æ›´å¤šçš„ç¬¬ä¸‰æ–¹é …ç›®ï¼ŒåŒ…æ‹¬ SDKã€æ’ä»¶ã€ä¸»é¡Œç‰ã€‚
+
+## 通訊
+
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵蓋的å•題,å¯ä»¥åœ¨æˆ‘們的 [Discord æœå‹™å™¨](https://discord.gg/Gitea) 上與我們è¯ç¹«ï¼Œæˆ–者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖å。
+
+## 作者
+
+- [ç¶è·è€…](https://github.com/orgs/go-gitea/people)
+- [è²¢ç»è€…](https://github.com/go-gitea/gitea/graphs/contributors)
+- [ç¿»è¯è€…](options/locale/TRANSLATORS)
+
+## 支æŒè€…
+
+æ„Ÿè¬æ‰€æœ‰æ”¯æŒè€…ï¼ ðŸ™ [[æˆç‚ºæ”¯æŒè€…](https://opencollective.com/gitea#backer)]
+
+
+
+## 贊助商
+
+é€šéŽæˆç‚ºè´ŠåŠ©å•†ä¾†æ”¯æŒé€™å€‹é …ç›®ã€‚æ‚¨çš„æ¨™èªŒå°‡é¡¯ç¤ºåœ¨é€™è£¡ï¼Œä¸¦å¸¶æœ‰éˆæŽ¥åˆ°æ‚¨çš„ç¶²ç«™ã€‚ [[æˆç‚ºè´ŠåЩ商](https://opencollective.com/gitea#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+## 常見å•題
+
+**Gitea 怎麼發音?**
+
+Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY)ï¼Œå°±åƒ "gi-tea" 一樣,g 是硬音。
+
+**ç‚ºä»€éº¼é€™å€‹é …ç›®æ²’æœ‰è¨—ç®¡åœ¨ Gitea 實例上?**
+
+我們æ£åœ¨ [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪裡å¯ä»¥æ‰¾åˆ°å®‰å…¨è£œä¸ï¼Ÿ**
+
+在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) ä¸ï¼Œæœç´¢é—œéµè©ž `SECURITY` 以找到安全補ä¸ã€‚
+
+## 許å¯è‰
+
+é€™å€‹é …ç›®æ˜¯æ ¹æ“š MIT 許å¯è‰æŽˆæ¬Šçš„。
+è«‹åƒé–± [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以ç²å–完整的許å¯è‰æ–‡æœ¬ã€‚
+
+## 進一æ¥ä¿¡æ¯
+
+
+å°‹æ‰¾ç•Œé¢æ¦‚述?查看這裡ï¼
+
+### 登錄/註冊é é¢
+
+
+
+
+### 用戶儀表æ¿
+
+
+
+
+
+
+### 用戶資料
+
+
+
+### 探索
+
+
+
+
+
+### 倉庫
+
+
+
+
+
+
+
+
+
+#### 倉庫å•題
+
+
+
+
+#### 倉庫拉å–請求
+
+
+
+
+
+
+#### 倉庫æ“作
+
+
+
+
+#### 倉庫活動
+
+
+
+
+
+
+### 組織
+
+
+
+
diff --git a/README_ZH.md b/README_ZH.md
deleted file mode 100644
index a2e36dc22f..0000000000
--- a/README_ZH.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Gitea
-
-[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
-[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
-[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
-[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
-[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
-[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
-[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
-[](https://opensource.org/licenses/MIT "License: MIT")
-[](https://gitpod.io/#https://github.com/go-gitea/gitea)
-[](https://crowdin.com/project/gitea "Crowdin")
-
-[View this document in English](./README.md)
-
-## ç›®æ ‡
-
-Gitea 的首è¦ç›®æ ‡æ˜¯åˆ›å»ºä¸€ä¸ªæžæ˜“安装,è¿è¡Œéžå¸¸å¿«é€Ÿï¼Œå®‰è£…和使用体验良好的自建 Git æœåŠ¡ã€‚æˆ‘ä»¬é‡‡ç”¨ Go 作为åŽç«¯è¯è¨€ï¼Œè¿™ä½¿æˆ‘们åªè¦ç”Ÿæˆä¸€ä¸ªå¯æ‰§è¡Œç¨‹åºå³å¯ã€‚并且他还支æŒè·¨å¹³å°ï¼Œæ”¯æŒ Linux, macOS å’Œ Windows 以åŠå„ç§æž¶æž„,除了 x86,amd64,还包括 ARM å’Œ PowerPC。
-
-å¦‚æžœä½ æƒ³è¯•ç”¨åœ¨çº¿æ¼”ç¤ºå’ŒæŠ¥å‘Šé—®é¢˜ï¼Œè¯·è®¿é—® [demo.gitea.com](https://demo.gitea.com/)。
-
-å¦‚æžœä½ æƒ³ä½¿ç”¨å…费的 Gitea æœåŠ¡ï¼ˆæœ‰ä»“åº“æ•°é‡é™åˆ¶ï¼‰ï¼Œè¯·è®¿é—® [gitea.com](https://gitea.com/user/login)。
-
-å¦‚æžœä½ æƒ³åœ¨ Gitea Cloud ä¸Šå¿«é€Ÿéƒ¨ç½²ä½ è‡ªå·±ç‹¬äº«çš„ Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始å…费试用。
-
-## æç¤º
-
-1. **开始贡献代ç 之å‰è¯·ç¡®ä¿ä½ å·²ç»çœ‹è¿‡äº† [贡献者å‘导(英文)](CONTRIBUTING.md)**.
-2. 所有的安全问题,请ç§ä¸‹å‘é€é‚®ä»¶ç»™ **security@gitea.io**。谢谢ï¼
-3. å¦‚æžœä½ è¦ä½¿ç”¨API,请å‚è§ [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
-
-## 文档
-
-关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation)ï¼Œå¦‚æžœæ²¡æœ‰æ‰¾åˆ°å¯¹åº”çš„æ–‡æ¡£ï¼Œä½ ä¹Ÿå¯ä»¥é€šè¿‡ [Discord - 英文](https://discord.gg/gitea) å’Œ QQ群 328432459 æ¥å’Œæˆ‘们交æµã€‚
-
-## 贡献æµç¨‹
-
-Fork -> Patch -> Push -> Pull Request
-
-## 翻译
-
-多è¯è¨€ç¿»è¯‘是基于Crowdin进行的.
-[](https://crowdin.com/project/gitea)
-
-## 作者
-
-* [Maintainers](https://github.com/orgs/go-gitea/people)
-* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
-* [Translators](options/locale/TRANSLATORS)
-
-## 授æƒè®¸å¯
-
-本项目采用 MIT å¼€æºæŽˆæƒè®¸å¯è¯ï¼Œå®Œæ•´çš„æŽˆæƒè¯´æ˜Žå·²æ”¾ç½®åœ¨ [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件ä¸ã€‚
-
-## 截图
-
-||||
-|:---:|:---:|:---:|
-||||
-||||
-||||
diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index 64c3b8b51c..d961444239 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -104,16 +104,6 @@
"path": "github.com/Azure/go-ntlmssp/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Microsoft\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.\n"
},
- {
- "name": "github.com/ClickHouse/ch-go",
- "path": "github.com/ClickHouse/ch-go/LICENSE",
- "licenseText": "Copyright 2016-2023 ClickHouse, Inc.\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016-2023 ClickHouse, Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
- {
- "name": "github.com/ClickHouse/clickhouse-go/v2",
- "path": "github.com/ClickHouse/clickhouse-go/v2/LICENSE",
- "licenseText": "Copyright 2016-2023 ClickHouse, Inc.\n\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016-2023 ClickHouse, Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
{
"name": "github.com/DataDog/zstd",
"path": "github.com/DataDog/zstd/LICENSE",
@@ -125,8 +115,8 @@
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
- "name": "github.com/RoaringBitmap/roaring",
- "path": "github.com/RoaringBitmap/roaring/LICENSE",
+ "name": "github.com/RoaringBitmap/roaring/v2",
+ "path": "github.com/RoaringBitmap/roaring/v2/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016 by the authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n================================================================================\n\nPortions of runcontainer.go are from the Go standard library, which is licensed\nunder:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n * Neither the name of Google Inc. nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
@@ -179,16 +169,6 @@
"path": "github.com/aws/aws-sdk-go-v2/service/codecommit/LICENSE.txt",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
- {
- "name": "github.com/aws/aws-sdk-go",
- "path": "github.com/aws/aws-sdk-go/LICENSE.txt",
- "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
- {
- "name": "github.com/aws/aws-sdk-go/internal/sync/singleflight",
- "path": "github.com/aws/aws-sdk-go/internal/sync/singleflight/LICENSE",
- "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- },
{
"name": "github.com/aws/smithy-go",
"path": "github.com/aws/smithy-go/LICENSE",
@@ -309,6 +289,16 @@
"path": "github.com/blevesearch/zapx/v16/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License."
},
+ {
+ "name": "github.com/bmatcuk/doublestar/v4",
+ "path": "github.com/bmatcuk/doublestar/v4/LICENSE",
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Bob Matcuk\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.\n\n"
+ },
+ {
+ "name": "github.com/bohde/codel",
+ "path": "github.com/bohde/codel/LICENSE",
+ "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, Rowan Bohde\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+ },
{
"name": "github.com/boombuler/barcode",
"path": "github.com/boombuler/barcode/LICENSE",
@@ -519,16 +509,6 @@
"path": "github.com/go-enry/go-enry/v2/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License."
},
- {
- "name": "github.com/go-faster/city",
- "path": "github.com/go-faster/city/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2018 tenfy\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.\n"
- },
- {
- "name": "github.com/go-faster/errors",
- "path": "github.com/go-faster/errors/LICENSE",
- "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- },
{
"name": "github.com/go-fed/httpsig",
"path": "github.com/go-fed/httpsig/LICENSE",
@@ -557,7 +537,7 @@
{
"name": "github.com/go-ldap/ldap/v3",
"path": "github.com/go-ldap/ldap/v3/LICENSE",
- "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2016 go-ldap Authors\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.\n"
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2024 go-ldap Authors\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.\n"
},
{
"name": "github.com/go-redsync/redsync/v4",
@@ -569,11 +549,6 @@
"path": "github.com/go-sql-driver/mysql/LICENSE",
"licenseText": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n"
},
- {
- "name": "github.com/go-testfixtures/testfixtures/v3",
- "path": "github.com/go-testfixtures/testfixtures/v3/LICENSE",
- "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Andrey Nering\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.\n"
- },
{
"name": "github.com/go-webauthn/webauthn",
"path": "github.com/go-webauthn/webauthn/LICENSE",
@@ -624,11 +599,6 @@
"path": "github.com/golang-sql/sqlexp/LICENSE",
"licenseText": "Copyright (c) 2017 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
- {
- "name": "github.com/golang/geo",
- "path": "github.com/golang/geo/LICENSE",
- "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
{
"name": "github.com/golang/groupcache/lru",
"path": "github.com/golang/groupcache/lru/LICENSE",
@@ -650,8 +620,13 @@
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
- "name": "github.com/google/go-github/v61/github",
- "path": "github.com/google/go-github/v61/github/LICENSE",
+ "name": "github.com/google/flatbuffers/go",
+ "path": "github.com/google/flatbuffers/go/LICENSE",
+ "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
+ {
+ "name": "github.com/google/go-github/v71/github",
+ "path": "github.com/google/go-github/v71/github/LICENSE",
"licenseText": "Copyright (c) 2013 The go-github AUTHORS. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
@@ -779,11 +754,6 @@
"path": "github.com/kevinburke/ssh_config/LICENSE",
"licenseText": "Copyright (c) 2017 Kevin Burke.\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\n===================\n\nThe lexer and parser borrow heavily from github.com/pelletier/go-toml. The\nlicense for that project is copied below.\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton\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.\n"
},
- {
- "name": "github.com/keybase/go-crypto",
- "path": "github.com/keybase/go-crypto/LICENSE",
- "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\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\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- },
{
"name": "github.com/klauspost/compress",
"path": "github.com/klauspost/compress/LICENSE",
@@ -860,13 +830,18 @@
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Yasuhiro Matsumoto\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.\n"
},
{
- "name": "github.com/meilisearch/meilisearch-go",
- "path": "github.com/meilisearch/meilisearch-go/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2020-2024 Meili SAS\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.\n"
+ "name": "github.com/mattn/go-shellwords",
+ "path": "github.com/mattn/go-shellwords/LICENSE",
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Yasuhiro Matsumoto\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.\n"
},
{
- "name": "github.com/mholt/acmez/v2",
- "path": "github.com/mholt/acmez/v2/LICENSE",
+ "name": "github.com/meilisearch/meilisearch-go",
+ "path": "github.com/meilisearch/meilisearch-go/LICENSE",
+ "licenseText": "MIT License\n\nCopyright (c) 2020-2025 Meili SAS\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.\n"
+ },
+ {
+ "name": "github.com/mholt/acmez/v3",
+ "path": "github.com/mholt/acmez/v3/LICENSE",
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
@@ -894,6 +869,11 @@
"path": "github.com/miekg/dns/LICENSE",
"licenseText": "BSD 3-Clause License\n\nCopyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
+ {
+ "name": "github.com/minio/crc64nvme",
+ "path": "github.com/minio/crc64nvme/LICENSE",
+ "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
{
"name": "github.com/minio/md5-simd",
"path": "github.com/minio/md5-simd/LICENSE",
@@ -964,11 +944,6 @@
"path": "github.com/opencontainers/image-spec/specs-go/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n Copyright 2016 The Linux Foundation.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
- {
- "name": "github.com/paulmach/orb",
- "path": "github.com/paulmach/orb/LICENSE.md",
- "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Paul Mach\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject 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, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
- },
{
"name": "github.com/pierrec/lz4/v4",
"path": "github.com/pierrec/lz4/v4/LICENSE",
@@ -977,7 +952,7 @@
{
"name": "github.com/pjbgf/sha1cd",
"path": "github.com/pjbgf/sha1cd/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2023 pjbgf\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
"name": "github.com/pkg/errors",
@@ -1059,21 +1034,11 @@
"path": "github.com/sassoftware/go-rpmutils/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
- {
- "name": "github.com/segmentio/asm",
- "path": "github.com/segmentio/asm/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2021 Segment\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.\n"
- },
{
"name": "github.com/sergi/go-diff/diffmatchpatch",
"path": "github.com/sergi/go-diff/diffmatchpatch/LICENSE",
"licenseText": "Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, 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\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\n"
},
- {
- "name": "github.com/shopspring/decimal",
- "path": "github.com/shopspring/decimal/LICENSE",
- "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Spring, Inc.\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\nall copies 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\nTHE SOFTWARE.\n\n- Based on https://github.com/oguzbilgic/fpd, which has the following license:\n\"\"\"\nThe MIT License (MIT)\n\nCopyright (c) 2013 Oguz Bilgic\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject 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, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\n"
- },
{
"name": "github.com/sirupsen/logrus",
"path": "github.com/sirupsen/logrus/LICENSE",
@@ -1115,9 +1080,14 @@
"licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License."
},
{
- "name": "github.com/urfave/cli/v2",
- "path": "github.com/urfave/cli/v2/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2022 urfave/cli maintainers\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.\n"
+ "name": "github.com/urfave/cli-docs/v3",
+ "path": "github.com/urfave/cli-docs/v3/LICENSE",
+ "licenseText": "MIT License\n\nCopyright (c) 2023 urfave/cli maintainers\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.\n"
+ },
+ {
+ "name": "github.com/urfave/cli/v3",
+ "path": "github.com/urfave/cli/v3/LICENSE",
+ "licenseText": "MIT License\n\nCopyright (c) 2023 urfave/cli maintainers\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.\n"
},
{
"name": "github.com/valyala/fastjson",
@@ -1127,7 +1097,7 @@
{
"name": "github.com/wneessen/go-mail",
"path": "github.com/wneessen/go-mail/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2022-2023 The go-mail Authors\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.\n"
+ "licenseText": "MIT License\n\nCopyright (c) 2022-2025 The go-mail Authors\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.\n"
},
{
"name": "github.com/wneessen/go-mail/smtp",
@@ -1139,21 +1109,11 @@
"path": "github.com/x448/float16/LICENSE",
"licenseText": "MIT License\n\nCopyright (c) 2019 Montgomery Edwardsâ´â´â¸ and Faye Amacker\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.\n\n"
},
- {
- "name": "github.com/xanzy/go-gitlab",
- "path": "github.com/xanzy/go-gitlab/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
{
"name": "github.com/xanzy/ssh-agent",
"path": "github.com/xanzy/ssh-agent/LICENSE",
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n"
},
- {
- "name": "github.com/xrash/smetrics",
- "path": "github.com/xrash/smetrics/LICENSE",
- "licenseText": "Copyright (C) 2016 Felipe da Cunha Gonçalves\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject 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, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
- },
{
"name": "github.com/yohcop/openid-go",
"path": "github.com/yohcop/openid-go/LICENSE",
@@ -1179,21 +1139,16 @@
"path": "github.com/zeebo/blake3/LICENSE",
"licenseText": "This work is released into the public domain with CC0 1.0.\n\n-------------------------------------------------------------------------------\n\nCreative Commons Legal Code\n\nCC0 1.0 Universal\n\n CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n i. the right to reproduce, adapt, distribute, perform, display,\n communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n subject to the limitations in paragraph 4(a), below;\n v. rights protecting the extraction, dissemination, use and reuse of data\n in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n European Parliament and of the Council of 11 March 1996 on the legal\n protection of databases, and under any national implementation\n thereof, including any amended or successor version of such\n directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n world based on applicable law or treaty, and any national\n implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n warranties of any kind concerning the Work, express, implied,\n statutory or otherwise, including without limitation warranties of\n title, merchantability, fitness for a particular purpose, non\n infringement, or the absence of latent or other defects, accuracy, or\n the present or absence of errors, whether or not discoverable, all to\n the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n that may apply to the Work or any use thereof, including without\n limitation any person's Copyright and Related Rights in the Work.\n Further, Affirmer disclaims responsibility for obtaining any necessary\n consents, permissions or other rights required for any use of the\n Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n party to this document and has no duty or obligation with respect to\n this CC0 or use of the Work.\n"
},
+ {
+ "name": "gitlab.com/gitlab-org/api/client-go",
+ "path": "gitlab.com/gitlab-org/api/client-go/LICENSE",
+ "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
{
"name": "go.etcd.io/bbolt",
"path": "go.etcd.io/bbolt/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Ben Johnson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject 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, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
},
- {
- "name": "go.opentelemetry.io/otel",
- "path": "go.opentelemetry.io/otel/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
- {
- "name": "go.opentelemetry.io/otel/trace",
- "path": "go.opentelemetry.io/otel/trace/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
{
"name": "go.uber.org/atomic",
"path": "go.uber.org/atomic/LICENSE.txt",
@@ -1209,6 +1164,11 @@
"path": "go.uber.org/zap/LICENSE",
"licenseText": "Copyright (c) 2016-2017 Uber Technologies, Inc.\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\nall copies 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\nTHE SOFTWARE.\n"
},
+ {
+ "name": "go.uber.org/zap/exp/zapslog",
+ "path": "go.uber.org/zap/exp/zapslog/LICENSE",
+ "licenseText": "Copyright (c) 2016-2024 Uber Technologies, Inc.\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\nall copies 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\nTHE SOFTWARE.\n"
+ },
{
"name": "golang.org/x/crypto",
"path": "golang.org/x/crypto/LICENSE",
diff --git a/build.go b/build.go
index 234579b514..e81ba54690 100644
--- a/build.go
+++ b/build.go
@@ -5,19 +5,10 @@
package main
-// Libraries that are included to vendor utilities used during build.
+// Libraries that are included to vendor utilities used during Makefile build.
// These libraries will not be included in a normal compilation.
import (
- // for embed
- _ "github.com/shurcooL/vfsgen"
-
- // for cover merge
- _ "golang.org/x/tools/cover"
-
// for vet
_ "code.gitea.io/gitea-vet"
-
- // for swagger
- _ "github.com/go-swagger/go-swagger/cmd/swagger"
)
diff --git a/build/generate-bindata.go b/build/generate-bindata.go
index 2fcb7c2f2a..2553770762 100644
--- a/build/generate-bindata.go
+++ b/build/generate-bindata.go
@@ -6,87 +6,22 @@
package main
import (
- "bytes"
- "crypto/sha1"
"fmt"
- "log"
- "net/http"
"os"
- "path/filepath"
- "strconv"
- "github.com/shurcooL/vfsgen"
+ "code.gitea.io/gitea/modules/assetfs"
)
-func needsUpdate(dir, filename string) (bool, []byte) {
- needRegen := false
- _, err := os.Stat(filename)
- if err != nil {
- needRegen = true
- }
-
- oldHash, err := os.ReadFile(filename + ".hash")
- if err != nil {
- oldHash = []byte{}
- }
-
- hasher := sha1.New()
-
- err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
- if err != nil {
- return err
- }
- info, err := d.Info()
- if err != nil {
- return err
- }
- _, _ = hasher.Write([]byte(d.Name()))
- _, _ = hasher.Write([]byte(info.ModTime().String()))
- _, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16)))
- return nil
- })
- if err != nil {
- return true, oldHash
- }
-
- newHash := hasher.Sum([]byte{})
-
- if bytes.Compare(oldHash, newHash) != 0 {
- return true, newHash
- }
-
- return needRegen, newHash
-}
-
func main() {
- if len(os.Args) < 4 {
- log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
+ if len(os.Args) != 3 {
+ fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}")
+ os.Exit(1)
}
- dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
- var useGlobalModTime bool
- if len(os.Args) == 5 {
- useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
+ dir, filename := os.Args[1], os.Args[2]
+ fmt.Printf("generating bindata for %s to %s\n", dir, filename)
+ if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil {
+ fmt.Printf("failed: %s\n", err.Error())
+ os.Exit(1)
}
-
- update, newHash := needsUpdate(dir, filename)
-
- if !update {
- fmt.Printf("bindata for %s already up-to-date\n", packageName)
- return
- }
-
- fmt.Printf("generating bindata for %s\n", packageName)
- var fsTemplates http.FileSystem = http.Dir(dir)
- err := vfsgen.Generate(fsTemplates, vfsgen.Options{
- PackageName: packageName,
- BuildTags: "bindata",
- VariableName: "Assets",
- Filename: filename,
- UseGlobalModTime: useGlobalModTime,
- })
- if err != nil {
- log.Fatalf("%v\n", err)
- }
- _ = os.WriteFile(filename+".hash", newHash, 0o666)
}
diff --git a/build/generate-licenses.go b/build/generate-licenses.go
deleted file mode 100644
index 66e1d37755..0000000000
--- a/build/generate-licenses.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build ignore
-
-package main
-
-import (
- "archive/tar"
- "compress/gzip"
- "crypto/md5"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "path"
- "path/filepath"
- "strings"
-
- "code.gitea.io/gitea/build/license"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/util"
-)
-
-func main() {
- var (
- prefix = "gitea-licenses"
- url = "https://api.github.com/repos/spdx/license-list-data/tarball"
- githubApiToken = ""
- githubUsername = ""
- destination = ""
- )
-
- flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
- flag.StringVar(&githubUsername, "username", "", "github username")
- flag.StringVar(&githubApiToken, "token", "", "github api token")
- flag.Parse()
-
- file, err := os.CreateTemp(os.TempDir(), prefix)
- if err != nil {
- log.Fatalf("Failed to create temp file. %s", err)
- }
-
- defer util.Remove(file.Name())
-
- if err := os.RemoveAll(destination); err != nil {
- log.Fatalf("Cannot clean destination folder: %v", err)
- }
-
- if err := os.MkdirAll(destination, 0o755); err != nil {
- log.Fatalf("Cannot create destination: %v", err)
- }
-
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- log.Fatalf("Failed to download archive. %s", err)
- }
-
- if len(githubApiToken) > 0 && len(githubUsername) > 0 {
- req.SetBasicAuth(githubUsername, githubApiToken)
- }
-
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- log.Fatalf("Failed to download archive. %s", err)
- }
-
- defer resp.Body.Close()
-
- if _, err := io.Copy(file, resp.Body); err != nil {
- log.Fatalf("Failed to copy archive to file. %s", err)
- }
-
- if _, err := file.Seek(0, 0); err != nil {
- log.Fatalf("Failed to reset seek on archive. %s", err)
- }
-
- gz, err := gzip.NewReader(file)
- if err != nil {
- log.Fatalf("Failed to gunzip the archive. %s", err)
- }
-
- tr := tar.NewReader(gz)
- aliasesFiles := make(map[string][]string)
- for {
- hdr, err := tr.Next()
-
- if err == io.EOF {
- break
- }
-
- if err != nil {
- log.Fatalf("Failed to iterate archive. %s", err)
- }
-
- if !strings.Contains(hdr.Name, "/text/") {
- continue
- }
-
- if filepath.Ext(hdr.Name) != ".txt" {
- continue
- }
-
- fileBaseName := filepath.Base(hdr.Name)
- licenseName := strings.TrimSuffix(fileBaseName, ".txt")
-
- if strings.HasPrefix(fileBaseName, "README") {
- continue
- }
-
- if strings.HasPrefix(fileBaseName, "deprecated_") {
- continue
- }
- out, err := os.Create(path.Join(destination, licenseName))
- if err != nil {
- log.Fatalf("Failed to create new file. %s", err)
- }
-
- defer out.Close()
-
- // some license files have same content, so we need to detect these files and create a convert map into a json file
- // Later we use this convert map to avoid adding same license content with different license name
- h := md5.New()
- // calculate md5 and write file in the same time
- r := io.TeeReader(tr, h)
- if _, err := io.Copy(out, r); err != nil {
- log.Fatalf("Failed to write new file. %s", err)
- } else {
- fmt.Printf("Written %s\n", out.Name())
-
- md5 := hex.EncodeToString(h.Sum(nil))
- aliasesFiles[md5] = append(aliasesFiles[md5], licenseName)
- }
- }
-
- // generate convert license name map
- licenseAliases := make(map[string]string)
- for _, fileNames := range aliasesFiles {
- if len(fileNames) > 1 {
- licenseName := license.GetLicenseNameFromAliases(fileNames)
- if licenseName == "" {
- // license name should not be empty as expected
- // if it is empty, we need to rewrite the logic of GetLicenseNameFromAliases
- log.Fatalf("GetLicenseNameFromAliases: license name is empty")
- }
- for _, fileName := range fileNames {
- licenseAliases[fileName] = licenseName
- }
- }
- }
- // save convert license name map to file
- b, err := json.Marshal(licenseAliases)
- if err != nil {
- log.Fatalf("Failed to create json bytes. %s", err)
- }
-
- licenseAliasesDestination := filepath.Join(destination, "etc", "license-aliases.json")
- if err := os.MkdirAll(filepath.Dir(licenseAliasesDestination), 0o755); err != nil {
- log.Fatalf("Failed to create directory for license aliases json file. %s", err)
- }
-
- f, err := os.Create(licenseAliasesDestination)
- if err != nil {
- log.Fatalf("Failed to create license aliases json file. %s", err)
- }
- defer f.Close()
-
- if _, err = f.Write(b); err != nil {
- log.Fatalf("Failed to write license aliases json file. %s", err)
- }
-
- fmt.Println("Done")
-}
diff --git a/build/license/aliasgenerator.go b/build/license/aliasgenerator.go
deleted file mode 100644
index 7de1e6fbd6..0000000000
--- a/build/license/aliasgenerator.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package license
-
-import "strings"
-
-func GetLicenseNameFromAliases(fnl []string) string {
- if len(fnl) == 0 {
- return ""
- }
-
- shortestItem := func(list []string) string {
- s := list[0]
- for _, l := range list[1:] {
- if len(l) < len(s) {
- s = l
- }
- }
- return s
- }
- allHasPrefix := func(list []string, s string) bool {
- for _, l := range list {
- if !strings.HasPrefix(l, s) {
- return false
- }
- }
- return true
- }
-
- sl := shortestItem(fnl)
- slv := strings.Split(sl, "-")
- var result string
- for i := len(slv); i >= 0; i-- {
- result = strings.Join(slv[:i], "-")
- if allHasPrefix(fnl, result) {
- return result
- }
- }
- return ""
-}
diff --git a/build/license/aliasgenerator_test.go b/build/license/aliasgenerator_test.go
deleted file mode 100644
index 239181b736..0000000000
--- a/build/license/aliasgenerator_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package license
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetLicenseNameFromAliases(t *testing.T) {
- tests := []struct {
- target string
- inputs []string
- }{
- {
- // real case which you can find in license-aliases.json
- target: "AGPL-1.0",
- inputs: []string{
- "AGPL-1.0-only",
- "AGPL-1.0-or-late",
- },
- },
- {
- target: "",
- inputs: []string{
- "APSL-1.0",
- "AGPL-1.0-only",
- "AGPL-1.0-or-late",
- },
- },
- }
-
- for _, tt := range tests {
- result := GetLicenseNameFromAliases(tt.inputs)
- assert.Equal(t, result, tt.target)
- }
-}
diff --git a/cmd/actions.go b/cmd/actions.go
index f582c16c81..2c51c6a1bc 100644
--- a/cmd/actions.go
+++ b/cmd/actions.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -17,7 +18,7 @@ var (
CmdActions = &cli.Command{
Name: "actions",
Usage: "Manage Gitea Actions",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdActionsGenRunnerToken,
},
}
@@ -38,10 +39,7 @@ var (
}
)
-func runGenerateActionsRunnerToken(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
scope := c.String("scope")
diff --git a/cmd/admin.go b/cmd/admin.go
index 6c9480e76e..559544edd3 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -23,7 +23,7 @@ var (
CmdAdmin = &cli.Command{
Name: "admin",
Usage: "Perform common administrative operations",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
subcmdRegenerate,
@@ -41,7 +41,7 @@ var (
subcmdRegenerate = &cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
@@ -50,15 +50,15 @@ var (
subcmdAuth = &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
- Subcommands: []*cli.Command{
- microcmdAuthAddOauth,
- microcmdAuthUpdateOauth,
- microcmdAuthAddLdapBindDn,
- microcmdAuthUpdateLdapBindDn,
- microcmdAuthAddLdapSimpleAuth,
- microcmdAuthUpdateLdapSimpleAuth,
- microcmdAuthAddSMTP,
- microcmdAuthUpdateSMTP,
+ Commands: []*cli.Command{
+ microcmdAuthAddOauth(),
+ microcmdAuthUpdateOauth(),
+ microcmdAuthAddLdapBindDn(),
+ microcmdAuthUpdateLdapBindDn(),
+ microcmdAuthAddLdapSimpleAuth(),
+ microcmdAuthUpdateLdapSimpleAuth(),
+ microcmdAuthAddSMTP(),
+ microcmdAuthUpdateSMTP(),
microcmdAuthList,
microcmdAuthDelete,
},
@@ -70,9 +70,9 @@ var (
Action: runSendMail,
Flags: []cli.Flag{
&cli.StringFlag{
- Name: "title",
- Usage: `a title of a message`,
- Value: "",
+ Name: "title",
+ Usage: "a title of a message",
+ Required: true,
},
&cli.StringFlag{
Name: "content",
@@ -86,17 +86,16 @@ var (
},
},
}
+)
- idFlag = &cli.Int64Flag{
+func idFlag() *cli.Int64Flag {
+ return &cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}
-)
-
-func runRepoSyncReleases(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
+}
+func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
@@ -107,7 +106,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ {
- repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page,
diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go
index 4777a92908..1a09366722 100644
--- a/cmd/admin_auth.go
+++ b/cmd/admin_auth.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -13,14 +14,14 @@ import (
"code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
microcmdAuthDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
- Flags: []cli.Flag{idFlag},
+ Flags: []cli.Flag{idFlag()},
Action: runDeleteAuth,
}
microcmdAuthList = &cli.Command{
@@ -56,10 +57,7 @@ var (
}
)
-func runListAuth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runListAuth(ctx context.Context, c *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
@@ -90,14 +88,11 @@ func runListAuth(c *cli.Context) error {
return nil
}
-func runDeleteAuth(c *cli.Context) error {
+func runDeleteAuth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go
index aff2a12855..069ad6600c 100644
--- a/cmd/admin_auth_ldap.go
+++ b/cmd/admin_auth_ldap.go
@@ -9,9 +9,10 @@ import (
"strings"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/ldap"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
type (
@@ -23,8 +24,8 @@ type (
}
)
-var (
- commonLdapCLIFlags = []cli.Flag{
+func commonLdapCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
@@ -102,8 +103,10 @@ var (
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
},
}
+}
- ldapBindDnCLIFlags = append(commonLdapCLIFlags,
+func ldapBindDnCLIFlags() []cli.Flag {
+ return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
@@ -127,50 +130,88 @@ var (
&cli.UintFlag{
Name: "page-size",
Usage: "Search page size.",
+ },
+ &cli.BoolFlag{
+ Name: "enable-groups",
+ Usage: "Enable LDAP groups",
+ },
+ &cli.StringFlag{
+ Name: "group-search-base-dn",
+ Usage: "The LDAP base DN at which group accounts will be searched for",
+ },
+ &cli.StringFlag{
+ Name: "group-member-attribute",
+ Usage: "Group attribute containing list of users",
+ },
+ &cli.StringFlag{
+ Name: "group-user-attribute",
+ Usage: "User attribute listed in group",
+ },
+ &cli.StringFlag{
+ Name: "group-filter",
+ Usage: "Verify group membership in LDAP",
+ },
+ &cli.StringFlag{
+ Name: "group-team-map",
+ Usage: "Map LDAP groups to Organization teams",
+ },
+ &cli.BoolFlag{
+ Name: "group-team-map-removal",
+ Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
})
+}
- ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
+func ldapSimpleAuthCLIFlags() []cli.Flag {
+ return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "user-dn",
Usage: "The user's DN.",
})
+}
- microcmdAuthAddLdapBindDn = &cli.Command{
+func microcmdAuthAddLdapBindDn() *cli.Command {
+ return &cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().addLdapBindDn(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().addLdapBindDn(ctx, cmd)
},
- Flags: ldapBindDnCLIFlags,
+ Flags: ldapBindDnCLIFlags(),
}
+}
- microcmdAuthUpdateLdapBindDn = &cli.Command{
+func microcmdAuthUpdateLdapBindDn() *cli.Command {
+ return &cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().updateLdapBindDn(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().updateLdapBindDn(ctx, cmd)
},
- Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
+ Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
}
+}
- microcmdAuthAddLdapSimpleAuth = &cli.Command{
+func microcmdAuthAddLdapSimpleAuth() *cli.Command {
+ return &cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().addLdapSimpleAuth(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().addLdapSimpleAuth(ctx, cmd)
},
- Flags: ldapSimpleAuthCLIFlags,
+ Flags: ldapSimpleAuthCLIFlags(),
}
+}
- microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
+func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
+ return &cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().updateLdapSimpleAuth(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().updateLdapSimpleAuth(ctx, cmd)
},
- Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
+ Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
}
-)
+}
// newAuthService creates a service with default functions.
func newAuthService() *authService {
@@ -182,8 +223,8 @@ func newAuthService() *authService {
}
}
-// parseAuthSource assigns values on authSource according to command line flags.
-func parseAuthSource(c *cli.Context, authSource *auth.Source) {
+// parseAuthSourceLdap assigns values on authSource according to command line flags.
+func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
if c.IsSet("name") {
authSource.Name = c.String("name")
}
@@ -199,10 +240,11 @@ func parseAuthSource(c *cli.Context, authSource *auth.Source) {
if c.IsSet("disable-synchronize-users") {
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
}
+ authSource.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
}
// parseLdapConfig assigns values on config according to command line flags.
-func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
+func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("name") {
config.Name = c.String("name")
}
@@ -215,7 +257,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("security-protocol") {
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
if !ok {
- return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
+ return fmt.Errorf("unknown security protocol name: %s", c.String("security-protocol"))
}
config.SecurityProtocol = p
}
@@ -270,8 +312,26 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("allow-deactivate-all") {
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
}
- if c.IsSet("skip-local-2fa") {
- config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
+ if c.IsSet("enable-groups") {
+ config.GroupsEnabled = c.Bool("enable-groups")
+ }
+ if c.IsSet("group-search-base-dn") {
+ config.GroupDN = c.String("group-search-base-dn")
+ }
+ if c.IsSet("group-member-attribute") {
+ config.GroupMemberUID = c.String("group-member-attribute")
+ }
+ if c.IsSet("group-user-attribute") {
+ config.UserUID = c.String("group-user-attribute")
+ }
+ if c.IsSet("group-filter") {
+ config.GroupFilter = c.String("group-filter")
+ }
+ if c.IsSet("group-team-map") {
+ config.GroupTeamMap = c.String("group-team-map")
+ }
+ if c.IsSet("group-team-map-removal") {
+ config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
}
return nil
}
@@ -289,32 +349,27 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
// getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
-func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
+func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
-
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return nil, err
}
if authSource.Type != authType {
- return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
+ return nil, fmt.Errorf("invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
}
return authSource, nil
}
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
-func (a *authService) addLdapBindDn(c *cli.Context) error {
+func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err
}
-
- ctx, cancel := installSignals()
- defer cancel()
-
if err := a.initDB(ctx); err != nil {
return err
}
@@ -327,7 +382,7 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
},
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -336,10 +391,7 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
}
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
-func (a *authService) updateLdapBindDn(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := a.initDB(ctx); err != nil {
return err
}
@@ -349,7 +401,7 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
return err
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -358,14 +410,11 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
}
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
-func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
+func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := a.initDB(ctx); err != nil {
return err
}
@@ -378,7 +427,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
},
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -387,10 +436,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
}
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
-func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := a.initDB(ctx); err != nil {
return err
}
@@ -400,7 +446,7 @@ func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
return err
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go
index 7791f3a9cc..2da7ebc573 100644
--- a/cmd/admin_auth_ldap_test.go
+++ b/cmd/admin_auth_ldap_test.go
@@ -8,17 +8,16 @@ import (
"testing"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/services/auth/source/ldap"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestAddLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -51,6 +50,13 @@ func TestAddLdapBindDn(t *testing.T) {
"--attributes-in-bind",
"--synchronize-users",
"--page-size", "99",
+ "--enable-groups",
+ "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
+ "--group-member-attribute", "memberUid",
+ "--group-user-attribute", "uid",
+ "--group-filter", "(|(cn=gitea_users)(cn=admins))",
+ "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ "--group-team-map-removal",
},
source: &auth.Source{
Type: auth.LDAP,
@@ -78,6 +84,13 @@ func TestAddLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
+ GroupsEnabled: true,
+ GroupDN: "ou=group,dc=full-domain-bind,dc=org",
+ GroupMemberUID: "memberUid",
+ UserUID: "uid",
+ GroupFilter: "(|(cn=gitea_users)(cn=admins))",
+ GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ GroupTeamMapRemoval: true,
},
},
},
@@ -121,7 +134,7 @@ func TestAddLdapBindDn(t *testing.T) {
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
"--email-attribute", "mail",
},
- errMsg: "Unknown security protocol name: zzzzz",
+ errMsg: "unknown security protocol name: zzzzz",
},
// case 3
{
@@ -215,22 +228,23 @@ func TestAddLdapBindDn(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthAddLdapBindDn.Flags
- app.Action = service.addLdapBindDn
+ app := cli.Command{
+ Flags: microcmdAuthAddLdapBindDn().Flags,
+ Action: service.addLdapBindDn,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -242,9 +256,7 @@ func TestAddLdapBindDn(t *testing.T) {
func TestAddLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -334,12 +346,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
"--name", "ldap (simple auth) source",
"--security-protocol", "zzzzz",
"--host", "ldap-server",
- "--port", "123",
+ "--port", "1234",
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
"--email-attribute", "mail",
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
},
- errMsg: "Unknown security protocol name: zzzzz",
+ errMsg: "unknown security protocol name: zzzzz",
},
// case 3
{
@@ -446,22 +458,23 @@ func TestAddLdapSimpleAuth(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
- app.Action = service.addLdapSimpleAuth
+ app := &cli.Command{
+ Flags: microcmdAuthAddLdapSimpleAuth().Flags,
+ Action: service.addLdapSimpleAuth,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -473,9 +486,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
func TestUpdateLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -510,6 +521,13 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--bind-password", "secret-bind-full",
"--synchronize-users",
"--page-size", "99",
+ "--enable-groups",
+ "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
+ "--group-member-attribute", "memberUid",
+ "--group-user-attribute", "uid",
+ "--group-filter", "(|(cn=gitea_users)(cn=admins))",
+ "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ "--group-team-map-removal",
},
id: 23,
existingAuthSource: &auth.Source{
@@ -545,6 +563,13 @@ func TestUpdateLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
+ GroupsEnabled: true,
+ GroupDN: "ou=group,dc=full-domain-bind,dc=org",
+ GroupMemberUID: "memberUid",
+ UserUID: "uid",
+ GroupFilter: "(|(cn=gitea_users)(cn=admins))",
+ GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ GroupTeamMapRemoval: true,
},
},
},
@@ -836,7 +861,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--id", "1",
"--security-protocol", "xxxxx",
},
- errMsg: "Unknown security protocol name: xxxxx",
+ errMsg: "unknown security protocol name: xxxxx",
},
// case 22
{
@@ -855,7 +880,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
Type: auth.OAuth2,
Cfg: &ldap.Source{},
},
- errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
+ errMsg: "invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
},
// case 24
{
@@ -897,7 +922,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -919,12 +944,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthUpdateLdapBindDn.Flags
- app.Action = service.updateLdapBindDn
-
+ app := cli.Command{
+ Flags: microcmdAuthUpdateLdapBindDn().Flags,
+ Action: service.updateLdapBindDn,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -936,9 +961,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
func TestUpdateLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -1229,7 +1252,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
"--id", "1",
"--security-protocol", "xxxxx",
},
- errMsg: "Unknown security protocol name: xxxxx",
+ errMsg: "unknown security protocol name: xxxxx",
},
// case 18
{
@@ -1248,7 +1271,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
Type: auth.PAM,
Cfg: &ldap.Source{},
},
- errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM",
+ errMsg: "invalid authentication type. expected: LDAP (simple auth), actual: PAM",
},
// case 20
{
@@ -1287,7 +1310,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -1309,12 +1332,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
- app.Action = service.updateLdapSimpleAuth
-
+ app := cli.Command{
+ Flags: microcmdAuthUpdateLdapSimpleAuth().Flags,
+ Action: service.updateLdapSimpleAuth,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go
index 8e6239ac33..d1aa753500 100644
--- a/cmd/admin_auth_oauth.go
+++ b/cmd/admin_auth_oauth.go
@@ -4,18 +4,20 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"net/url"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/oauth2"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var (
- oauthCLIFlags = []cli.Flag{
+func oauthCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -120,23 +122,34 @@ var (
Usage: "Activate automatic team membership removal depending on groups",
},
}
+}
- microcmdAuthAddOauth = &cli.Command{
- Name: "add-oauth",
- Usage: "Add new Oauth authentication source",
- Action: runAddOauth,
- Flags: oauthCLIFlags,
+func microcmdAuthAddOauth() *cli.Command {
+ return &cli.Command{
+ Name: "add-oauth",
+ Usage: "Add new Oauth authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runAddOauth(ctx, cmd)
+ },
+ Flags: oauthCLIFlags(),
}
+}
- microcmdAuthUpdateOauth = &cli.Command{
- Name: "update-oauth",
- Usage: "Update existing Oauth authentication source",
- Action: runUpdateOauth,
- Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
+func microcmdAuthUpdateOauth() *cli.Command {
+ return &cli.Command{
+ Name: "update-oauth",
+ Usage: "Update existing Oauth authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runUpdateOauth(ctx, cmd)
+ },
+ Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of authentication source",
+ }}, oauthCLIFlags()[1:]...)...),
}
-)
+}
-func parseOAuth2Config(c *cli.Context) *oauth2.Source {
+func parseOAuth2Config(c *cli.Command) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
@@ -156,7 +169,6 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"),
- SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
@@ -168,11 +180,8 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
}
}
-func runAddOauth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
+ if err := a.initDB(ctx); err != nil {
return err
}
@@ -184,27 +193,25 @@ func runAddOauth(c *cli.Context) error {
}
}
- return auth_model.CreateSource(ctx, &auth_model.Source{
- Type: auth_model.OAuth2,
- Name: c.String("name"),
- IsActive: true,
- Cfg: config,
+ return a.createAuthSource(ctx, &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: c.String("name"),
+ IsActive: true,
+ Cfg: config,
+ TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
})
}
-func runUpdateOauth(c *cli.Context) error {
+func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+ if err := a.initDB(ctx); err != nil {
return err
}
- source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
+ source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -294,6 +301,6 @@ func runUpdateOauth(c *cli.Context) error {
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
-
- return auth_model.UpdateSource(ctx, source)
+ source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
+ return a.updateAuthSource(ctx, source)
}
diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go
new file mode 100644
index 0000000000..df1bd9c1a6
--- /dev/null
+++ b/cmd/admin_auth_oauth_test.go
@@ -0,0 +1,333 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/services/auth/source/oauth2"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestAddOauth(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ source *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "valid config",
+ args: []string{
+ "--name", "test",
+ "--provider", "github",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Scopes: []string{},
+ Provider: "github",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with openid connect",
+ args: []string{
+ "--name", "test",
+ "--provider", "openidConnect",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ "--auto-discover-url", "https://example.com",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Scopes: []string{},
+ Provider: "openidConnect",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ OpenIDConnectAutoDiscoveryURL: "https://example.com",
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ args: []string{
+ "--name", "test",
+ "--provider", "gitlab",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ "--use-custom-urls", "true",
+ "--custom-token-url", "https://example.com/token",
+ "--custom-auth-url", "https://example.com/auth",
+ "--custom-profile-url", "https://example.com/profile",
+ "--custom-email-url", "https://example.com/email",
+ "--custom-tenant-id", "some_tenant",
+ "--icon-url", "https://example.com/icon",
+ "--scopes", "scope1,scope2",
+ "--skip-local-2fa", "true",
+ "--required-claim-name", "claim_name",
+ "--required-claim-value", "claim_value",
+ "--group-claim-name", "group_name",
+ "--admin-group", "admin",
+ "--restricted-group", "restricted",
+ "--group-team-map", `{"group1": [1,2]}`,
+ "--group-team-map-removal=true",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://example.com/token",
+ AuthURL: "https://example.com/auth",
+ ProfileURL: "https://example.com/profile",
+ EmailURL: "https://example.com/email",
+ Tenant: "some_tenant",
+ },
+ IconURL: "https://example.com/icon",
+ Scopes: []string{"scope1", "scope2"},
+ RequiredClaimName: "claim_name",
+ RequiredClaimValue: "claim_value",
+ GroupClaimName: "group_name",
+ AdminGroup: "admin",
+ RestrictedGroup: "restricted",
+ GroupTeamMap: `{"group1": [1,2]}`,
+ GroupTeamMapRemoval: true,
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ var createdSource *auth_model.Source
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ createdSource = source
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthAddOauth().Flags,
+ Action: a.runAddOauth,
+ }
+
+ args := []string{"oauth-test"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.source, createdSource)
+ }
+ })
+ }
+}
+
+func TestUpdateOauth(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ id int64
+ existingAuthSource *auth_model.Source
+ authSource *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing id",
+ args: []string{
+ "--name", "test",
+ },
+ errMsg: "--id flag is missing",
+ },
+ {
+ name: "valid config",
+ id: 1,
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "github",
+ ClientID: "old_key",
+ ClientSecret: "old_secret",
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--provider", "gitlab",
+ "--key", "new_key",
+ "--secret", "new_secret",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "new_key",
+ ClientSecret: "new_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{},
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ id: 1,
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "old_key",
+ ClientSecret: "old_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://old.example.com/token",
+ AuthURL: "https://old.example.com/auth",
+ ProfileURL: "https://old.example.com/profile",
+ EmailURL: "https://old.example.com/email",
+ Tenant: "old_tenant",
+ },
+ IconURL: "https://old.example.com/icon",
+ Scopes: []string{"old_scope1", "old_scope2"},
+ RequiredClaimName: "old_claim_name",
+ RequiredClaimValue: "old_claim_value",
+ GroupClaimName: "old_group_name",
+ AdminGroup: "old_admin",
+ RestrictedGroup: "old_restricted",
+ GroupTeamMap: `{"old_group1": [1,2]}`,
+ GroupTeamMapRemoval: true,
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--provider", "github",
+ "--key", "new_key",
+ "--secret", "new_secret",
+ "--use-custom-urls", "true",
+ "--custom-token-url", "https://example.com/token",
+ "--custom-auth-url", "https://example.com/auth",
+ "--custom-profile-url", "https://example.com/profile",
+ "--custom-email-url", "https://example.com/email",
+ "--custom-tenant-id", "new_tenant",
+ "--icon-url", "https://example.com/icon",
+ "--scopes", "scope1,scope2",
+ "--skip-local-2fa=true",
+ "--required-claim-name", "claim_name",
+ "--required-claim-value", "claim_value",
+ "--group-claim-name", "group_name",
+ "--admin-group", "admin",
+ "--restricted-group", "restricted",
+ "--group-team-map", `{"group1": [1,2]}`,
+ "--group-team-map-removal=false",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "github",
+ ClientID: "new_key",
+ ClientSecret: "new_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://example.com/token",
+ AuthURL: "https://example.com/auth",
+ ProfileURL: "https://example.com/profile",
+ EmailURL: "https://example.com/email",
+ Tenant: "new_tenant",
+ },
+ IconURL: "https://example.com/icon",
+ Scopes: []string{"scope1", "scope2"},
+ RequiredClaimName: "claim_name",
+ RequiredClaimValue: "claim_value",
+ GroupClaimName: "group_name",
+ AdminGroup: "admin",
+ RestrictedGroup: "restricted",
+ GroupTeamMap: `{"group1": [1,2]}`,
+ GroupTeamMapRemoval: false,
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
+ return &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ CustomURLMapping: &oauth2.CustomURLMapping{},
+ },
+ TwoFactorPolicy: "skip",
+ }, nil
+ },
+ updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.authSource, source)
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthUpdateOauth().Flags,
+ Action: a.runUpdateOauth,
+ }
+
+ args := []string{"oauth-test"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_smtp.go
similarity index 67%
rename from cmd/admin_auth_stmp.go
rename to cmd/admin_auth_smtp.go
index d724746905..93e0587fc3 100644
--- a/cmd/admin_auth_stmp.go
+++ b/cmd/admin_auth_smtp.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"strings"
@@ -11,11 +12,11 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/smtp"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var (
- smtpCLIFlags = []cli.Flag{
+func smtpCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -38,12 +39,10 @@ var (
&cli.BoolFlag{
Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
- Value: true,
},
&cli.BoolFlag{
Name: "skip-verify",
Usage: "Skip TLS verify.",
- Value: true,
},
&cli.StringFlag{
Name: "helo-hostname",
@@ -53,7 +52,6 @@ var (
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
- Value: true,
},
&cli.StringFlag{
Name: "allowed-domains",
@@ -63,7 +61,6 @@ var (
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
- Value: true,
},
&cli.BoolFlag{
Name: "active",
@@ -71,23 +68,34 @@ var (
Value: true,
},
}
+}
- microcmdAuthAddSMTP = &cli.Command{
- Name: "add-smtp",
- Usage: "Add new SMTP authentication source",
- Action: runAddSMTP,
- Flags: smtpCLIFlags,
+func microcmdAuthUpdateSMTP() *cli.Command {
+ return &cli.Command{
+ Name: "update-smtp",
+ Usage: "Update existing SMTP authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runUpdateSMTP(ctx, cmd)
+ },
+ Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of authentication source",
+ }}, smtpCLIFlags()[1:]...)...),
}
+}
- microcmdAuthUpdateSMTP = &cli.Command{
- Name: "update-smtp",
- Usage: "Update existing SMTP authentication source",
- Action: runUpdateSMTP,
- Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
+func microcmdAuthAddSMTP() *cli.Command {
+ return &cli.Command{
+ Name: "add-smtp",
+ Usage: "Add new SMTP authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runAddSMTP(ctx, cmd)
+ },
+ Flags: smtpCLIFlags(),
}
-)
+}
-func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
+func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@@ -117,17 +125,11 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
if c.IsSet("disable-helo") {
conf.DisableHelo = c.Bool("disable-helo")
}
- if c.IsSet("skip-local-2fa") {
- conf.SkipLocalTwoFA = c.Bool("skip-local-2fa")
- }
return nil
}
-func runAddSMTP(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
+ if err := a.initDB(ctx); err != nil {
return err
}
@@ -155,27 +157,25 @@ func runAddSMTP(c *cli.Context) error {
smtpConfig.Auth = "PLAIN"
}
- return auth_model.CreateSource(ctx, &auth_model.Source{
- Type: auth_model.SMTP,
- Name: c.String("name"),
- IsActive: active,
- Cfg: &smtpConfig,
+ return a.createAuthSource(ctx, &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: c.String("name"),
+ IsActive: active,
+ Cfg: &smtpConfig,
+ TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
})
}
-func runUpdateSMTP(c *cli.Context) error {
+func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+ if err := a.initDB(ctx); err != nil {
return err
}
- source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
+ source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -195,6 +195,6 @@ func runUpdateSMTP(c *cli.Context) error {
}
source.Cfg = smtpConfig
-
- return auth_model.UpdateSource(ctx, source)
+ source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
+ return a.updateAuthSource(ctx, source)
}
diff --git a/cmd/admin_auth_smtp_test.go b/cmd/admin_auth_smtp_test.go
new file mode 100644
index 0000000000..e54e01830c
--- /dev/null
+++ b/cmd/admin_auth_smtp_test.go
@@ -0,0 +1,271 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/services/auth/source/smtp"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestAddSMTP(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ source *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing name",
+ args: []string{
+ "--host", "localhost",
+ "--port", "25",
+ },
+ errMsg: "name must be set",
+ },
+ {
+ name: "missing host",
+ args: []string{
+ "--name", "test",
+ "--port", "25",
+ },
+ errMsg: "host must be set",
+ },
+ {
+ name: "missing port",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ },
+ errMsg: "port must be set",
+ },
+ {
+ name: "valid config",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "localhost",
+ Port: 25,
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ "--auth-type", "LOGIN",
+ "--force-smtps",
+ "--skip-verify",
+ "--helo-hostname", "example.com",
+ "--disable-helo=true",
+ "--allowed-domains", "example.com,example.org",
+ "--skip-local-2fa",
+ "--active=false",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: false,
+ Cfg: &smtp.Source{
+ Auth: "LOGIN",
+ Host: "localhost",
+ Port: 25,
+ ForceSMTPS: true,
+ SkipVerify: true,
+ HeloHostname: "example.com",
+ DisableHelo: true,
+ AllowedDomains: "example.com,example.org",
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.source, source)
+ return nil
+ },
+ }
+
+ cmd := &cli.Command{
+ Flags: microcmdAuthAddSMTP().Flags,
+ Action: a.runAddSMTP,
+ }
+
+ args := []string{"smtp-test"}
+ args = append(args, tc.args...)
+
+ t.Log(args)
+ err := cmd.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestUpdateSMTP(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ existingAuthSource *auth_model.Source
+ authSource *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing id",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ errMsg: "--id flag is missing",
+ },
+ {
+ name: "valid config",
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "old host",
+ Port: 26,
+ },
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "localhost",
+ Port: 25,
+ },
+ },
+ },
+ {
+ name: "valid config with options",
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "old host",
+ Port: 26,
+ HeloHostname: "old.example.com",
+ AllowedDomains: "old.example.com",
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ "--auth-type", "LOGIN",
+ "--force-smtps",
+ "--skip-verify",
+ "--helo-hostname", "example.com",
+ "--disable-helo",
+ "--allowed-domains", "example.com,example.org",
+ "--skip-local-2fa",
+ "--active=false",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: false,
+ Cfg: &smtp.Source{
+ Auth: "LOGIN",
+ Host: "localhost",
+ Port: 25,
+ ForceSMTPS: true,
+ SkipVerify: true,
+ HeloHostname: "example.com",
+ DisableHelo: true,
+ AllowedDomains: "example.com,example.org",
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
+ return &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ },
+ }, nil
+ },
+
+ updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.authSource, source)
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthUpdateSMTP().Flags,
+ Action: a.runUpdateSMTP,
+ }
+ args := []string{"smtp-tests"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go
index ab769f6d0c..a5f1bd5105 100644
--- a/cmd/admin_regenerate.go
+++ b/cmd/admin_regenerate.go
@@ -4,11 +4,13 @@
package cmd
import (
+ "context"
+
"code.gitea.io/gitea/modules/graceful"
asymkey_service "code.gitea.io/gitea/services/asymkey"
repo_service "code.gitea.io/gitea/services/repository"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -25,20 +27,14 @@ var (
}
)
-func runRegenerateHooks(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
}
-func runRegenerateKeys(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_user.go b/cmd/admin_user.go
index 967a6ed88a..3a24c3e56f 100644
--- a/cmd/admin_user.go
+++ b/cmd/admin_user.go
@@ -4,18 +4,18 @@
package cmd
import (
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var subcmdUser = &cli.Command{
Name: "user",
Usage: "Modify users",
- Subcommands: []*cli.Command{
- microcmdUserCreate,
+ Commands: []*cli.Command{
+ microcmdUserCreate(),
microcmdUserList,
- microcmdUserChangePassword,
- microcmdUserDelete,
+ microcmdUserChangePassword(),
+ microcmdUserDelete(),
microcmdUserGenerateAccessToken,
- microcmdUserMustChangePassword,
+ microcmdUserMustChangePassword(),
},
}
diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go
index f1ed46e70b..c27905b4db 100644
--- a/cmd/admin_user_change_password.go
+++ b/cmd/admin_user_change_password.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
@@ -13,44 +14,41 @@ import (
"code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserChangePassword = &cli.Command{
- Name: "change-password",
- Usage: "Change a user's password",
- Action: runChangePassword,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "username",
- Aliases: []string{"u"},
- Value: "",
- Usage: "The user to change password for",
+func microcmdUserChangePassword() *cli.Command {
+ return &cli.Command{
+ Name: "change-password",
+ Usage: "Change a user's password",
+ Action: runChangePassword,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "username",
+ Aliases: []string{"u"},
+ Usage: "The user to change password for",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "password",
+ Aliases: []string{"p"},
+ Usage: "New password to set for user",
+ Required: true,
+ },
+ &cli.BoolFlag{
+ Name: "must-change-password",
+ Usage: "User must change password (can be disabled by --must-change-password=false)",
+ Value: true,
+ },
},
- &cli.StringFlag{
- Name: "password",
- Aliases: []string{"p"},
- Value: "",
- Usage: "New password to set for user",
- },
- &cli.BoolFlag{
- Name: "must-change-password",
- Usage: "User must change password (can be disabled by --must-change-password=false)",
- Value: true,
- },
- },
+ }
}
-func runChangePassword(c *cli.Context) error {
- if err := argsSet(c, "username", "password"); err != nil {
- return err
- }
-
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
- return err
+func runChangePassword(ctx context.Context, c *cli.Command) error {
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
user, err := user_model.GetUserByName(ctx, c.String("username"))
diff --git a/cmd/admin_user_change_password_test.go b/cmd/admin_user_change_password_test.go
new file mode 100644
index 0000000000..17d0382af7
--- /dev/null
+++ b/cmd/admin_user_change_password_test.go
@@ -0,0 +1,91 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestChangePasswordCommand(t *testing.T) {
+ ctx := t.Context()
+
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ }()
+
+ t.Run("change password successfully", func(t *testing.T) {
+ // defer func() {
+ // require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ // }()
+ // Prepare test user
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+
+ // load test user
+ userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+
+ // Change the password
+ err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"})
+ require.NoError(t, err)
+
+ // Verify the password has been changed
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.NotEqual(t, userBase.Passwd, user.Passwd)
+ assert.NotEqual(t, userBase.Salt, user.Salt)
+
+ // Additional check for must-change-password flag
+ require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"}))
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, user.MustChangePassword)
+
+ require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"}))
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, user.MustChangePassword)
+ })
+
+ t.Run("failure cases", func(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ expectedErr string
+ }{
+ {
+ name: "user does not exist",
+ args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"},
+ expectedErr: "user does not exist",
+ },
+ {
+ name: "missing username",
+ args: []string{"change-password", "--password", "newpassword"},
+ expectedErr: `"username" not set`,
+ },
+ {
+ name: "missing password",
+ args: []string{"change-password", "--username", "testuser"},
+ expectedErr: `"password" not set`,
+ },
+ {
+ name: "too short password",
+ args: []string{"change-password", "--username", "testuser", "--password", "1"},
+ expectedErr: "password is not long enough",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ err := microcmdUserChangePassword().Run(ctx, tc.args)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.expectedErr)
+ })
+ }
+ })
+}
diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go
index 106d14b25a..cbdb5f90e2 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
@@ -15,69 +16,110 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserCreate = &cli.Command{
- Name: "create",
- Usage: "Create a new user in database",
- Action: runCreateUser,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "name",
- Usage: "Username. DEPRECATED: use username instead",
+func microcmdUserCreate() *cli.Command {
+ return &cli.Command{
+ Name: "create",
+ Usage: "Create a new user in database",
+ Action: runCreateUser,
+ MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
+ {
+ Flags: [][]cli.Flag{
+ {
+ &cli.StringFlag{
+ Name: "name",
+ Usage: "Username. DEPRECATED: use username instead",
+ },
+ &cli.StringFlag{
+ Name: "username",
+ Usage: "Username",
+ },
+ },
+ },
+ Required: true,
+ },
},
- &cli.StringFlag{
- Name: "username",
- Usage: "Username",
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "user-type",
+ Usage: "Set user's type: individual or bot",
+ Value: "individual",
+ },
+ &cli.StringFlag{
+ Name: "password",
+ Usage: "User password",
+ },
+ &cli.StringFlag{
+ Name: "email",
+ Usage: "User email address",
+ Required: true,
+ },
+ &cli.BoolFlag{
+ Name: "admin",
+ Usage: "User is an admin",
+ },
+ &cli.BoolFlag{
+ Name: "random-password",
+ Usage: "Generate a random password for the user",
+ },
+ &cli.BoolFlag{
+ Name: "must-change-password",
+ Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
+ HideDefault: true,
+ },
+ &cli.IntFlag{
+ Name: "random-password-length",
+ Usage: "Length of the random password to be generated",
+ Value: 12,
+ },
+ &cli.BoolFlag{
+ Name: "access-token",
+ Usage: "Generate access token for the user",
+ },
+ &cli.StringFlag{
+ Name: "access-token-name",
+ Usage: `Name of the generated access token`,
+ Value: "gitea-admin",
+ },
+ &cli.StringFlag{
+ Name: "access-token-scopes",
+ Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
+ Value: "all",
+ },
+ &cli.BoolFlag{
+ Name: "restricted",
+ Usage: "Make a restricted user account",
+ },
+ &cli.StringFlag{
+ Name: "fullname",
+ Usage: `The full, human-readable name of the user`,
+ },
},
- &cli.StringFlag{
- Name: "password",
- Usage: "User password",
- },
- &cli.StringFlag{
- Name: "email",
- Usage: "User email address",
- },
- &cli.BoolFlag{
- Name: "admin",
- Usage: "User is an admin",
- },
- &cli.BoolFlag{
- Name: "random-password",
- Usage: "Generate a random password for the user",
- },
- &cli.BoolFlag{
- Name: "must-change-password",
- Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
- DisableDefaultText: true,
- },
- &cli.IntFlag{
- Name: "random-password-length",
- Usage: "Length of the random password to be generated",
- Value: 12,
- },
- &cli.BoolFlag{
- Name: "access-token",
- Usage: "Generate access token for the user",
- },
- &cli.BoolFlag{
- Name: "restricted",
- Usage: "Make a restricted user account",
- },
- },
+ }
}
-func runCreateUser(c *cli.Context) error {
- if err := argsSet(c, "email"); err != nil {
- return err
- }
+func runCreateUser(ctx context.Context, c *cli.Command) error {
+ // this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
+ // duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
+ setting.LoadSettings()
- if c.IsSet("name") && c.IsSet("username") {
- return errors.New("cannot set both --name and --username flags")
+ userTypes := map[string]user_model.UserType{
+ "individual": user_model.UserTypeIndividual,
+ "bot": user_model.UserTypeBot,
}
- if !c.IsSet("name") && !c.IsSet("username") {
- return errors.New("one of --name or --username flags must be set")
+ userType, ok := userTypes[c.String("user-type")]
+ if !ok {
+ return fmt.Errorf("invalid user type: %s", c.String("user-type"))
+ }
+ if userType != user_model.UserTypeIndividual {
+ // Some other commands like "change-password" also only support individual users.
+ // It needs to clarify the "password" behavior for bot users in the future.
+ // At the moment, we do not allow setting password for bot users.
+ if c.IsSet("password") || c.IsSet("random-password") {
+ return errors.New("password can only be set for individual users")
+ }
}
if c.IsSet("password") && c.IsSet("random-password") {
@@ -89,16 +131,12 @@ func runCreateUser(c *cli.Context) error {
username = c.String("username")
} else {
username = c.String("name")
- _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
+ _, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
}
- ctx := c.Context
if !setting.IsInTesting {
- // FIXME: need to refactor the "installSignals/initDB" related code later
+ // FIXME: need to refactor the "initDB" related code later
// it doesn't make sense to call it in (almost) every command action function
- var cancel context.CancelFunc
- ctx, cancel = installSignals()
- defer cancel()
if err := initDB(ctx); err != nil {
return err
}
@@ -114,16 +152,19 @@ func runCreateUser(c *cli.Context) error {
return err
}
fmt.Printf("generated random password is '%s'\n", password)
- } else {
+ } else if userType == user_model.UserTypeIndividual {
return errors.New("must set either password or random-password flag")
}
isAdmin := c.Bool("admin")
mustChangePassword := true // always default to true
if c.IsSet("must-change-password") {
+ if userType != user_model.UserTypeIndividual {
+ return errors.New("must-change-password flag can only be set for individual users")
+ }
// if the flag is set, use the value provided by the user
mustChangePassword = c.Bool("must-change-password")
- } else {
+ } else if userType == user_model.UserTypeIndividual {
// check whether there are users in the database
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
if err != nil {
@@ -147,10 +188,12 @@ func runCreateUser(c *cli.Context) error {
u := &user_model.User{
Name: username,
Email: c.String("email"),
- Passwd: password,
IsAdmin: isAdmin,
+ Type: userType,
+ Passwd: password,
MustChangePassword: mustChangePassword,
Visibility: visibility,
+ FullName: c.String("fullname"),
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -158,23 +201,40 @@ func runCreateUser(c *cli.Context) error {
IsRestricted: restricted,
}
+ var accessTokenName string
+ var accessTokenScope auth_model.AccessTokenScope
+ if c.IsSet("access-token") {
+ accessTokenName = strings.TrimSpace(c.String("access-token-name"))
+ if accessTokenName == "" {
+ return errors.New("access-token-name cannot be empty")
+ }
+ var err error
+ accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
+ if err != nil {
+ return fmt.Errorf("invalid access token scope provided: %w", err)
+ }
+ if !accessTokenScope.HasPermissionScope() {
+ return errors.New("access token does not have any permission")
+ }
+ } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
+ return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+ }
+
+ // arguments should be prepared before creating the user & access token, in case there is anything wrong
+
+ // create the user
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
return fmt.Errorf("CreateUser: %w", err)
}
+ fmt.Printf("New user '%s' has been successfully created!\n", username)
- if c.Bool("access-token") {
- t := &auth_model.AccessToken{
- Name: "gitea-admin",
- UID: u.ID,
- }
-
+ // create the access token
+ if accessTokenScope != "" {
+ t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
if err := auth_model.NewAccessToken(ctx, t); err != nil {
return err
}
-
fmt.Printf("Access token was successfully created... %s\n", t.Token)
}
-
- fmt.Printf("New user '%s' has been successfully created!\n", username)
return nil
}
diff --git a/cmd/admin_user_create_test.go b/cmd/admin_user_create_test.go
index 83754e97b1..437e07d9a2 100644
--- a/cmd/admin_user_create_test.go
+++ b/cmd/admin_user_create_test.go
@@ -8,37 +8,127 @@ import (
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestAdminUserCreate(t *testing.T) {
- app := NewMainApp(AppVersion{})
-
reset := func() {
- assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
- assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
}
- type createCheck struct{ IsAdmin, MustChangePassword bool }
- createUser := func(name, args string) createCheck {
- assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
- u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
- return createCheck{u.IsAdmin, u.MustChangePassword}
+ t.Run("MustChangePassword", func(t *testing.T) {
+ type check struct {
+ IsAdmin bool
+ MustChangePassword bool
+ }
+
+ createCheck := func(name, args string) check {
+ require.NoError(t, microcmdUserCreate().Run(t.Context(), strings.Fields(fmt.Sprintf("create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
+ return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
+ }
+ reset()
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password")
+
+ reset()
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password")
+
+ reset()
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password"))
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin"))
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false"))
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", ""))
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
+ })
+
+ createUser := func(name string, args ...string) error {
+ return microcmdUserCreate().Run(t.Context(), append([]string{"create", "--username", name, "--email", name + "@gitea.local"}, args...))
}
- reset()
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
- reset()
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
+ t.Run("UserType", func(t *testing.T) {
+ reset()
+ assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
+ assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
+ assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
- reset()
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
+ assert.NoError(t, createUser("u", "--user-type", "bot"))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
+ assert.Equal(t, user_model.UserTypeBot, u.Type)
+ assert.Empty(t, u.Passwd)
+ })
+
+ t.Run("AccessToken", func(t *testing.T) {
+ // no generated access token
+ reset()
+ assert.NoError(t, createUser("u", "--random-password"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+
+ // using "--access-token" only means "all" access
+ reset()
+ assert.NoError(t, createUser("u", "--random-password", "--access-token"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
+ accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
+ hasScopes, err := accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
+ assert.NoError(t, err)
+ assert.True(t, hasScopes)
+
+ // using "--access-token" with name & scopes
+ reset()
+ assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
+ accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
+ hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadUser)
+ assert.NoError(t, err)
+ assert.True(t, hasScopes)
+ hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
+ assert.NoError(t, err)
+ assert.False(t, hasScopes)
+
+ // using "--access-token-name" without "--access-token"
+ reset()
+ err = createUser("u", "--random-password", "--access-token-name", "new-token-name")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+
+ // using "--access-token-scopes" without "--access-token"
+ reset()
+ err = createUser("u", "--random-password", "--access-token-scopes", "read:issue")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+
+ // empty permission
+ reset()
+ err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access token does not have any permission")
+ })
+
+ t.Run("UserFields", func(t *testing.T) {
+ reset()
+ assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last"))
+ unittest.AssertExistsAndLoadBean(t, &user_model.User{
+ Name: "u-FullNameWithSpace",
+ LowerName: "u-fullnamewithspace",
+ FullName: "First O'Middle Last",
+ Email: "u-FullNameWithSpace@gitea.local",
+ })
+
+ assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", ""))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"})
+ assert.Empty(t, u.FullName)
+ })
}
diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go
index 520557554a..f91041577c 100644
--- a/cmd/admin_user_delete.go
+++ b/cmd/admin_user_delete.go
@@ -4,53 +4,56 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"strings"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
user_service "code.gitea.io/gitea/services/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserDelete = &cli.Command{
- Name: "delete",
- Usage: "Delete specific user by id, name or email",
- Flags: []cli.Flag{
- &cli.Int64Flag{
- Name: "id",
- Usage: "ID of user of the user to delete",
+func microcmdUserDelete() *cli.Command {
+ return &cli.Command{
+ Name: "delete",
+ Usage: "Delete specific user by id, name or email",
+ Flags: []cli.Flag{
+ &cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of user of the user to delete",
+ },
+ &cli.StringFlag{
+ Name: "username",
+ Aliases: []string{"u"},
+ Usage: "Username of the user to delete",
+ },
+ &cli.StringFlag{
+ Name: "email",
+ Aliases: []string{"e"},
+ Usage: "Email of the user to delete",
+ },
+ &cli.BoolFlag{
+ Name: "purge",
+ Usage: "Purge user, all their repositories, organizations and comments",
+ },
},
- &cli.StringFlag{
- Name: "username",
- Aliases: []string{"u"},
- Usage: "Username of the user to delete",
- },
- &cli.StringFlag{
- Name: "email",
- Aliases: []string{"e"},
- Usage: "Email of the user to delete",
- },
- &cli.BoolFlag{
- Name: "purge",
- Usage: "Purge user, all their repositories, organizations and comments",
- },
- },
- Action: runDeleteUser,
+ Action: runDeleteUser,
+ }
}
-func runDeleteUser(c *cli.Context) error {
+func runDeleteUser(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return errors.New("You must provide the id, username or email of a user to delete")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
- return err
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
if err := storage.Init(); err != nil {
@@ -70,11 +73,11 @@ func runDeleteUser(c *cli.Context) error {
return err
}
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
- return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
+ return fmt.Errorf("the user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
}
if c.IsSet("id") && user.ID != c.Int64("id") {
- return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
+ return fmt.Errorf("the user %s does not match the provided id %d", user.Name, c.Int64("id"))
}
return user_service.DeleteUser(ctx, user, c.Bool("purge"))
diff --git a/cmd/admin_user_delete_test.go b/cmd/admin_user_delete_test.go
new file mode 100644
index 0000000000..d0330582d7
--- /dev/null
+++ b/cmd/admin_user_delete_test.go
@@ -0,0 +1,111 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "strconv"
+ "strings"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestAdminUserDelete(t *testing.T) {
+ ctx := t.Context()
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
+ }()
+
+ setupTestUser := func(t *testing.T) {
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ }
+
+ t.Run("delete user by id", func(t *testing.T) {
+ setupTestUser(t)
+
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by username", func(t *testing.T) {
+ setupTestUser(t)
+
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by email", func(t *testing.T) {
+ setupTestUser(t)
+
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by all 3 attributes", func(t *testing.T) {
+ setupTestUser(t)
+
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+}
+
+func TestAdminUserDeleteFailure(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ expectedErr string
+ }{
+ {
+ name: "no user to delete",
+ args: []string{"delete", "--username", "nonexistentuser"},
+ expectedErr: "user does not exist",
+ },
+ {
+ name: "user exists but provided username does not match",
+ args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"},
+ expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername",
+ },
+ {
+ name: "user exists but provided id does not match",
+ args: []string{"delete", "--username", "testuser", "--id", "999"},
+ expectedErr: "the user testuser does not match the provided id 999",
+ },
+ {
+ name: "no required flags are provided",
+ args: []string{"delete"},
+ expectedErr: "You must provide the id, username or email of a user to delete",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := t.Context()
+ if strings.Contains(tc.name, "user exists") {
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ }
+
+ err := microcmdUserDelete().Run(ctx, tc.args)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.expectedErr)
+ })
+
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
+ }
+}
diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go
index 6c2c10494e..61064fdef4 100644
--- a/cmd/admin_user_generate_access_token.go
+++ b/cmd/admin_user_generate_access_token.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"errors"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var microcmdUserGenerateAccessToken = &cli.Command{
@@ -34,21 +35,18 @@ var microcmdUserGenerateAccessToken = &cli.Command{
},
&cli.StringFlag{
Name: "scopes",
- Value: "",
- Usage: "Comma separated list of scopes to apply to access token",
+ Value: "all",
+ Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
},
},
Action: runGenerateAccessToken,
}
-func runGenerateAccessToken(c *cli.Context) error {
+func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
- return errors.New("You must provide a username to generate a token for")
+ return errors.New("you must provide a username to generate a token for")
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := initDB(ctx); err != nil {
return err
}
@@ -77,6 +75,9 @@ func runGenerateAccessToken(c *cli.Context) error {
if err != nil {
return fmt.Errorf("invalid access token scope provided: %w", err)
}
+ if !accessTokenScope.HasPermissionScope() {
+ return errors.New("access token does not have any permission")
+ }
t.Scope = accessTokenScope
// create the token
diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go
index 4c2b26d1df..e3d345e2f2 100644
--- a/cmd/admin_user_list.go
+++ b/cmd/admin_user_list.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"fmt"
"os"
"text/tabwriter"
user_model "code.gitea.io/gitea/models/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var microcmdUserList = &cli.Command{
@@ -25,10 +26,7 @@ var microcmdUserList = &cli.Command{
},
}
-func runListUsers(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runListUsers(ctx context.Context, c *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_user_must_change_password.go b/cmd/admin_user_must_change_password.go
index 2794414259..8521853dc1 100644
--- a/cmd/admin_user_must_change_password.go
+++ b/cmd/admin_user_must_change_password.go
@@ -4,40 +4,41 @@
package cmd
import (
+ "context"
"errors"
"fmt"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserMustChangePassword = &cli.Command{
- Name: "must-change-password",
- Usage: "Set the must change password flag for the provided users or all users",
- Action: runMustChangePassword,
- Flags: []cli.Flag{
- &cli.BoolFlag{
- Name: "all",
- Aliases: []string{"A"},
- Usage: "All users must change password, except those explicitly excluded with --exclude",
+func microcmdUserMustChangePassword() *cli.Command {
+ return &cli.Command{
+ Name: "must-change-password",
+ Usage: "Set the must change password flag for the provided users or all users",
+ Action: runMustChangePassword,
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "all",
+ Aliases: []string{"A"},
+ Usage: "All users must change password, except those explicitly excluded with --exclude",
+ },
+ &cli.StringSliceFlag{
+ Name: "exclude",
+ Aliases: []string{"e"},
+ Usage: "Do not change the must-change-password flag for these users",
+ },
+ &cli.BoolFlag{
+ Name: "unset",
+ Usage: "Instead of setting the must-change-password flag, unset it",
+ },
},
- &cli.StringSliceFlag{
- Name: "exclude",
- Aliases: []string{"e"},
- Usage: "Do not change the must-change-password flag for these users",
- },
- &cli.BoolFlag{
- Name: "unset",
- Usage: "Instead of setting the must-change-password flag, unset it",
- },
- },
+ }
}
-func runMustChangePassword(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runMustChangePassword(ctx context.Context, c *cli.Command) error {
if c.NArg() == 0 && !c.IsSet("all") {
return errors.New("either usernames or --all must be provided")
}
@@ -46,8 +47,10 @@ func runMustChangePassword(c *cli.Context) error {
all := c.Bool("all")
exclude := c.StringSlice("exclude")
- if err := initDB(ctx); err != nil {
- return err
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
diff --git a/cmd/admin_user_must_change_password_test.go b/cmd/admin_user_must_change_password_test.go
new file mode 100644
index 0000000000..a6611fdc04
--- /dev/null
+++ b/cmd/admin_user_must_change_password_test.go
@@ -0,0 +1,78 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMustChangePassword(t *testing.T) {
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ }()
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ // Reset password change flag
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
+ require.NoError(t, err)
+
+ testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Make all users change password
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.True(t, testUserExclude.MustChangePassword)
+
+ // Reset password change flag but exclude all tested users
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.True(t, testUserExclude.MustChangePassword)
+
+ // Reset password change flag by listing multiple users
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Exclude a user from all user
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Unset a flag for single user
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+}
diff --git a/cmd/cert.go b/cmd/cert.go
index 38241d71a3..53b4f9dcb4 100644
--- a/cmd/cert.go
+++ b/cmd/cert.go
@@ -6,6 +6,7 @@
package cmd
import (
+ "context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -13,6 +14,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
+ "fmt"
"log"
"math/big"
"net"
@@ -20,47 +22,59 @@ import (
"strings"
"time"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-// CmdCert represents the available cert sub-command.
-var CmdCert = &cli.Command{
- Name: "cert",
- Usage: "Generate self-signed certificate",
- Description: `Generate a self-signed X.509 certificate for a TLS server.
+// cmdCert represents the available cert sub-command.
+func cmdCert() *cli.Command {
+ return &cli.Command{
+ Name: "cert",
+ Usage: "Generate self-signed certificate",
+ Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
- Action: runCert,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "host",
- Value: "",
- Usage: "Comma-separated hostnames and IPs to generate a certificate for",
+ Action: runCert,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "host",
+ Usage: "Comma-separated hostnames and IPs to generate a certificate for",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "ecdsa-curve",
+ Value: "",
+ Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
+ },
+ &cli.IntFlag{
+ Name: "rsa-bits",
+ Value: 3072,
+ Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
+ },
+ &cli.StringFlag{
+ Name: "start-date",
+ Value: "",
+ Usage: "Creation date formatted as Jan 1 15:04:05 2011",
+ },
+ &cli.DurationFlag{
+ Name: "duration",
+ Value: 365 * 24 * time.Hour,
+ Usage: "Duration that certificate is valid for",
+ },
+ &cli.BoolFlag{
+ Name: "ca",
+ Usage: "whether this cert should be its own Certificate Authority",
+ },
+ &cli.StringFlag{
+ Name: "out",
+ Value: "cert.pem",
+ Usage: "Path to the file where there certificate will be saved",
+ },
+ &cli.StringFlag{
+ Name: "keyout",
+ Value: "key.pem",
+ Usage: "Path to the file where there certificate key will be saved",
+ },
},
- &cli.StringFlag{
- Name: "ecdsa-curve",
- Value: "",
- Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
- },
- &cli.IntFlag{
- Name: "rsa-bits",
- Value: 3072,
- Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
- },
- &cli.StringFlag{
- Name: "start-date",
- Value: "",
- Usage: "Creation date formatted as Jan 1 15:04:05 2011",
- },
- &cli.DurationFlag{
- Name: "duration",
- Value: 365 * 24 * time.Hour,
- Usage: "Duration that certificate is valid for",
- },
- &cli.BoolFlag{
- Name: "ca",
- Usage: "whether this cert should be its own Certificate Authority",
- },
- },
+ }
}
func publicKey(priv any) any {
@@ -89,11 +103,7 @@ func pemBlockForKey(priv any) *pem.Block {
}
}
-func runCert(c *cli.Context) error {
- if err := argsSet(c, "host"); err != nil {
- return err
- }
-
+func runCert(_ context.Context, c *cli.Command) error {
var priv any
var err error
switch c.String("ecdsa-curve") {
@@ -108,17 +118,17 @@ func runCert(c *cli.Context) error {
case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
- log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
+ err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
}
if err != nil {
- log.Fatalf("Failed to generate private key: %v", err)
+ return fmt.Errorf("failed to generate private key: %w", err)
}
var notBefore time.Time
if startDate := c.String("start-date"); startDate != "" {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
if err != nil {
- log.Fatalf("Failed to parse creation date: %v", err)
+ return fmt.Errorf("failed to parse creation date %w", err)
}
} else {
notBefore = time.Now()
@@ -129,7 +139,7 @@ func runCert(c *cli.Context) error {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
- log.Fatalf("Failed to generate serial number: %v", err)
+ return fmt.Errorf("failed to generate serial number: %w", err)
}
template := x509.Certificate{
@@ -146,8 +156,8 @@ func runCert(c *cli.Context) error {
BasicConstraintsValid: true,
}
- hosts := strings.Split(c.String("host"), ",")
- for _, h := range hosts {
+ hosts := strings.SplitSeq(c.String("host"), ",")
+ for h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
@@ -162,35 +172,35 @@ func runCert(c *cli.Context) error {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil {
- log.Fatalf("Failed to create certificate: %v", err)
+ return fmt.Errorf("failed to create certificate: %w", err)
}
- certOut, err := os.Create("cert.pem")
+ certOut, err := os.Create(c.String("out"))
if err != nil {
- log.Fatalf("Failed to open cert.pem for writing: %v", err)
+ return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
}
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
- log.Fatalf("Failed to encode certificate: %v", err)
+ return fmt.Errorf("failed to encode certificate: %w", err)
}
err = certOut.Close()
if err != nil {
- log.Fatalf("Failed to write cert: %v", err)
+ return fmt.Errorf("failed to write cert: %w", err)
}
- log.Println("Written cert.pem")
+ fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out"))
- keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
+ keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
- log.Fatalf("Failed to open key.pem for writing: %v", err)
+ return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
}
err = pem.Encode(keyOut, pemBlockForKey(priv))
if err != nil {
- log.Fatalf("Failed to encode key: %v", err)
+ return fmt.Errorf("failed to encode key: %w", err)
}
err = keyOut.Close()
if err != nil {
- log.Fatalf("Failed to write key: %v", err)
+ return fmt.Errorf("failed to write key: %w", err)
}
- log.Println("Written key.pem")
+ fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout"))
return nil
}
diff --git a/cmd/cert_test.go b/cmd/cert_test.go
new file mode 100644
index 0000000000..4242d8915b
--- /dev/null
+++ b/cmd/cert_test.go
@@ -0,0 +1,123 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCertCommand(t *testing.T) {
+ cases := []struct {
+ name string
+ args []string
+ }{
+ {
+ name: "RSA cert generation",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--rsa-bits", "2048",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ {
+ name: "ECDSA cert generation",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--ecdsa-curve", "P256",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ {
+ name: "mixed host, certificate authority",
+ args: []string{
+ "cert-test",
+ "--host", "localhost,127.0.0.1",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ app := cmdCert()
+ tempDir := t.TempDir()
+
+ certFile := filepath.Join(tempDir, "cert.pem")
+ keyFile := filepath.Join(tempDir, "key.pem")
+
+ err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
+ require.NoError(t, err)
+
+ assert.FileExists(t, certFile)
+ assert.FileExists(t, keyFile)
+ })
+ }
+}
+
+func TestCertCommandFailures(t *testing.T) {
+ cases := []struct {
+ name string
+ args []string
+ errMsg string
+ }{
+ {
+ name: "Start Date Parsing failure",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--start-date", "invalid-date",
+ },
+ errMsg: "parsing time",
+ },
+ {
+ name: "Unknown curve",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--ecdsa-curve", "invalid-curve",
+ },
+ errMsg: "unrecognized elliptic curve",
+ },
+ {
+ name: "Key generation failure",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--rsa-bits", "invalid-bits",
+ },
+ },
+ {
+ name: "Missing parameters",
+ args: []string{
+ "cert-test",
+ },
+ errMsg: `"host" not set`,
+ },
+ }
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ app := cmdCert()
+ tempDir := t.TempDir()
+
+ certFile := filepath.Join(tempDir, "cert.pem")
+ keyFile := filepath.Join(tempDir, "key.pem")
+ err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
+ require.Error(t, err)
+ if c.errMsg != "" {
+ assert.ErrorContains(t, err, c.errMsg)
+ }
+ assert.NoFileExists(t, certFile)
+ assert.NoFileExists(t, keyFile)
+ })
+ }
+}
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 423dce2674..5b96bcbf9a 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -18,20 +18,19 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context.
-func argsSet(c *cli.Context, args ...string) error {
+func argsSet(c *cli.Command, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
return errors.New(a + " is not set")
}
- if util.IsEmptyString(c.String(a)) {
+ if c.Value(a) == nil {
return errors.New(a + " is required")
}
}
@@ -109,7 +108,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
}
-func globalBool(c *cli.Context, name string) bool {
+func globalBool(c *cli.Command, name string) bool {
for _, ctx := range c.Lineage() {
if ctx.Bool(name) {
return true
@@ -120,8 +119,8 @@ func globalBool(c *cli.Context, name string) bool {
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
-func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
- return func(c *cli.Context) error {
+func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) {
+ return func(ctx context.Context, c *cli.Command) (context.Context, error) {
level := defaultLevel
if globalBool(c, "quiet") {
level = log.FATAL
@@ -130,6 +129,16 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error
level = log.TRACE
}
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
- return nil
+ return ctx, nil
}
}
+
+func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
+ // Dirty patch for urfave/cli's strange design.
+ // "./gitea bad-cmd" should not start the web server.
+ rootArgs := cmd.Root().Args().Slice()
+ if len(rootArgs) != 0 && rootArgs[0] != cmd.Name {
+ return rootArgs[0], false
+ }
+ return "", true
+}
diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go
new file mode 100644
index 0000000000..a36d05c76e
--- /dev/null
+++ b/cmd/cmd_test.go
@@ -0,0 +1,38 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestDefaultCommand(t *testing.T) {
+ test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
+ called := false
+ cmd := &cli.Command{
+ DefaultCommand: "test",
+ Commands: []*cli.Command{
+ {
+ Name: "test",
+ Action: func(ctx context.Context, command *cli.Command) error {
+ retName, retValid := isValidDefaultSubCommand(command)
+ assert.Equal(t, expectedRetName, retName)
+ assert.Equal(t, expectedRetValid, retValid)
+ called = true
+ return nil
+ },
+ },
+ },
+ }
+ assert.NoError(t, cmd.Run(t.Context(), args))
+ assert.True(t, called)
+ }
+ test(t, []string{"./gitea"}, "", true)
+ test(t, []string{"./gitea", "test"}, "", true)
+ test(t, []string{"./gitea", "other"}, "other", false)
+}
diff --git a/cmd/docs.go b/cmd/docs.go
index 605d02e3ef..098c0e9a8a 100644
--- a/cmd/docs.go
+++ b/cmd/docs.go
@@ -4,11 +4,13 @@
package cmd
import (
+ "context"
"fmt"
"os"
"strings"
- "github.com/urfave/cli/v2"
+ cli_docs "github.com/urfave/cli-docs/v3"
+ "github.com/urfave/cli/v3"
)
// CmdDocs represents the available docs sub-command.
@@ -30,16 +32,16 @@ var CmdDocs = &cli.Command{
},
}
-func runDocs(ctx *cli.Context) error {
- docs, err := ctx.App.ToMarkdown()
- if ctx.Bool("man") {
- docs, err = ctx.App.ToMan()
+func runDocs(_ context.Context, cmd *cli.Command) error {
+ docs, err := cli_docs.ToMarkdown(cmd.Root())
+ if cmd.Bool("man") {
+ docs, err = cli_docs.ToMan(cmd.Root())
}
if err != nil {
return err
}
- if !ctx.Bool("man") {
+ if !cmd.Bool("man") {
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040
@@ -51,8 +53,8 @@ func runDocs(ctx *cli.Context) error {
}
out := os.Stdout
- if ctx.String("output") != "" {
- fi, err := os.Create(ctx.String("output"))
+ if cmd.String("output") != "" {
+ fi, err := os.Create(cmd.String("output"))
if err != nil {
return err
}
diff --git a/cmd/doctor.go b/cmd/doctor.go
index e433f4adc5..9e0fcbf877 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"fmt"
golog "log"
"os"
@@ -19,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/doctor"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
"xorm.io/xorm"
)
@@ -29,7 +30,7 @@ var CmdDoctor = &cli.Command{
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
@@ -92,16 +93,13 @@ You should back-up your database before doing this and ensure that your database
Action: runRecreateTable,
}
-func runRecreateTable(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
+func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
// Redirect the default golog to here
golog.SetFlags(0)
golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
- debug := ctx.Bool("debug")
+ debug := cmd.Bool("debug")
setting.MustInstalled()
setting.LoadDBSetting()
@@ -112,15 +110,15 @@ func runRecreateTable(ctx *cli.Context) error {
}
setting.Database.LogSQL = debug
- if err := db.InitEngine(stdCtx); err != nil {
+ if err := db.InitEngine(ctx); err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}
- args := ctx.Args()
- names := make([]string, 0, ctx.NArg())
- for i := 0; i < ctx.NArg(); i++ {
+ args := cmd.Args()
+ names := make([]string, 0, cmd.NArg())
+ for i := 0; i < cmd.NArg(); i++ {
names = append(names, args.Get(i))
}
@@ -130,24 +128,25 @@ func runRecreateTable(ctx *cli.Context) error {
}
recreateTables := migrate_base.RecreateTables(beans...)
- return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
- if err := migrations.EnsureUpToDate(x); err != nil {
+ return db.InitEngineWithMigration(ctx, func(ctx context.Context, x *xorm.Engine) error {
+ if err := migrations.EnsureUpToDate(ctx, x); err != nil {
return err
}
return recreateTables(x)
})
}
-func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
+func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
// Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
- logFile := ctx.String("log-file")
- if logFile == "" {
+ logFile := cmd.String("log-file")
+ switch logFile {
+ case "":
return // if no doctor log-file is set, do not show any log from default logger
- } else if logFile == "-" {
+ case "-":
setupConsoleLogger(log.TRACE, colorize, os.Stdout)
- } else {
+ default:
logFile, _ = filepath.Abs(logFile)
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
@@ -159,23 +158,20 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
}
}
-func runDoctorCheck(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
+func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
colorize := log.CanColorStdout
- if ctx.IsSet("color") {
- colorize = ctx.Bool("color")
+ if cmd.IsSet("color") {
+ colorize = cmd.Bool("color")
}
- setupDoctorDefaultLogger(ctx, colorize)
+ setupDoctorDefaultLogger(cmd, colorize)
// Finally redirect the default golang's log to here
golog.SetFlags(0)
golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
- if ctx.IsSet("list") {
+ if cmd.IsSet("list") {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
doctor.SortChecks(doctor.Checks)
@@ -193,12 +189,12 @@ func runDoctorCheck(ctx *cli.Context) error {
}
var checks []*doctor.Check
- if ctx.Bool("all") {
+ if cmd.Bool("all") {
checks = make([]*doctor.Check, len(doctor.Checks))
copy(checks, doctor.Checks)
- } else if ctx.IsSet("run") {
- addDefault := ctx.Bool("default")
- runNamesSet := container.SetOf(ctx.StringSlice("run")...)
+ } else if cmd.IsSet("run") {
+ addDefault := cmd.Bool("default")
+ runNamesSet := container.SetOf(cmd.StringSlice("run")...)
for _, check := range doctor.Checks {
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
checks = append(checks, check)
@@ -215,5 +211,5 @@ func runDoctorCheck(ctx *cli.Context) error {
}
}
}
- return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
+ return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks)
}
diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go
index 48c835ad0e..8cb718d383 100644
--- a/cmd/doctor_convert.go
+++ b/cmd/doctor_convert.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// cmdDoctorConvert represents the available convert sub-command.
@@ -21,11 +22,8 @@ var cmdDoctorConvert = &cli.Command{
Action: runDoctorConvert,
}
-func runDoctorConvert(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go
index 3e1ff299c5..da942b38b6 100644
--- a/cmd/doctor_test.go
+++ b/cmd/doctor_test.go
@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/services/doctor"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestDoctorRun(t *testing.T) {
@@ -22,12 +22,13 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true,
})
- app := cli.NewApp()
- app.Commands = []*cli.Command{cmdDoctorCheck}
- err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
+ app := &cli.Command{
+ Commands: []*cli.Command{cmdDoctorCheck},
+ }
+ err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
assert.NoError(t, err)
- err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
+ err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
- err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
+ err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
}
diff --git a/cmd/dump.go b/cmd/dump.go
index ececc80f72..ed19e3d4bf 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -5,7 +5,7 @@
package cmd
import (
- "fmt"
+ "context"
"os"
"path"
"path/filepath"
@@ -21,7 +21,7 @@ import (
"gitea.com/go-chi/session"
"github.com/mholt/archiver/v3"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdDump represents the available dump sub-command.
@@ -93,7 +93,7 @@ var CmdDump = &cli.Command{
},
&cli.StringFlag{
Name: "type",
- Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
+ Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
},
},
}
@@ -102,17 +102,17 @@ func fatal(format string, args ...any) {
log.Fatal(format, args...)
}
-func runDump(ctx *cli.Context) error {
+func runDump(ctx context.Context, cmd *cli.Command) error {
setting.MustInstalled()
- quite := ctx.Bool("quiet")
- verbose := ctx.Bool("verbose")
+ quite := cmd.Bool("quiet")
+ verbose := cmd.Bool("verbose")
if verbose && quite {
fatal("Option --quiet and --verbose cannot both be set")
}
// outFileName is either "-" or a file name (will be made absolute)
- outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
+ outFileName, outType := dump.PrepareFileNameAndType(cmd.String("file"), cmd.String("type"))
if outType == "" {
fatal("Invalid output type")
}
@@ -137,10 +137,7 @@ func runDump(ctx *cli.Context) error {
setting.DisableLoggerInit()
setting.LoadSettings() // cannot access session settings otherwise
- stdCtx, cancel := installSignals()
- defer cancel()
-
- err := db.InitEngine(stdCtx)
+ err := db.InitEngine(ctx)
if err != nil {
return err
}
@@ -166,7 +163,7 @@ func runDump(ctx *cli.Context) error {
}
dumper.GlobalExcludeAbsPath(outFileName)
- if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
+ if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") {
log.Info("Skip dumping local repositories")
} else {
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
@@ -174,7 +171,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to include repositories: %v", err)
}
- if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
+ if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") {
log.Info("Skip dumping LFS data")
} else if !setting.LFS.StartServer {
log.Info("LFS isn't enabled. Skip dumping LFS data")
@@ -189,12 +186,12 @@ func runDump(ctx *cli.Context) error {
}
}
- if ctx.Bool("skip-db") {
+ if cmd.Bool("skip-db") {
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
dumper.GlobalExcludeAbsPath(setting.Database.Path)
log.Info("Skipping database")
} else {
- tmpDir := ctx.String("tempdir")
+ tmpDir := cmd.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
@@ -210,7 +207,7 @@ func runDump(ctx *cli.Context) error {
}
}()
- targetDBType := ctx.String("database")
+ targetDBType := cmd.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
@@ -231,7 +228,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to include specified app.ini: %v", err)
}
- if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
+ if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") {
log.Info("Skipping custom directory")
} else {
customDir, err := os.Stat(setting.CustomPath)
@@ -264,7 +261,7 @@ func runDump(ctx *cli.Context) error {
excludes = append(excludes, opts.ProviderConfig)
}
- if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
+ if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {
excludes = append(excludes, setting.Indexer.RepoPath)
excludes = append(excludes, setting.Indexer.IssuePath)
}
@@ -279,7 +276,7 @@ func runDump(ctx *cli.Context) error {
}
}
- if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
+ if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") {
log.Info("Skip dumping attachment data")
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat()
@@ -291,7 +288,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to dump attachments: %v", err)
}
- if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
+ if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") {
log.Info("Skip dumping package data")
} else if !setting.Packages.Enabled {
log.Info("Packages isn't enabled. Skip dumping package data")
@@ -308,7 +305,7 @@ func runDump(ctx *cli.Context) error {
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
// ensuring that it's clear the dump is skipped whether the directory's initialized
// yet or not.
- if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
+ if cmd.IsSet("skip-log") && cmd.Bool("skip-log") {
log.Info("Skip dumping log files")
} else {
isExist, err := util.IsExist(setting.Log.RootPath)
diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go
index 3a24cf6c5f..a75b2d1b94 100644
--- a/cmd/dump_repo.go
+++ b/cmd/dump_repo.go
@@ -19,7 +19,7 @@ import (
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/migrations"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdDumpRepository represents the available dump repository sub-command.
@@ -79,11 +79,13 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runDumpRepository(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
+func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
+ setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
- if err := initDB(stdCtx); err != nil {
+ setting.DisableLoggerInit()
+ setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
+
+ if err := initDB(ctx); err != nil {
return err
}
@@ -100,8 +102,8 @@ func runDumpRepository(ctx *cli.Context) error {
var (
serviceType structs.GitServiceType
- cloneAddr = ctx.String("clone_addr")
- serviceStr = ctx.String("git_service")
+ cloneAddr = cmd.String("clone_addr")
+ serviceStr = cmd.String("git_service")
)
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
@@ -119,13 +121,13 @@ func runDumpRepository(ctx *cli.Context) error {
opts := base.MigrateOptions{
GitServiceType: serviceType,
CloneAddr: cloneAddr,
- AuthUsername: ctx.String("auth_username"),
- AuthPassword: ctx.String("auth_password"),
- AuthToken: ctx.String("auth_token"),
- RepoName: ctx.String("repo_name"),
+ AuthUsername: cmd.String("auth_username"),
+ AuthPassword: cmd.String("auth_password"),
+ AuthToken: cmd.String("auth_token"),
+ RepoName: cmd.String("repo_name"),
}
- if len(ctx.String("units")) == 0 {
+ if len(cmd.String("units")) == 0 {
opts.Wiki = true
opts.Issues = true
opts.Milestones = true
@@ -135,8 +137,8 @@ func runDumpRepository(ctx *cli.Context) error {
opts.PullRequests = true
opts.ReleaseAssets = true
} else {
- units := strings.Split(ctx.String("units"), ",")
- for _, unit := range units {
+ units := strings.SplitSeq(cmd.String("units"), ",")
+ for unit := range units {
switch strings.ToLower(strings.TrimSpace(unit)) {
case "":
continue
@@ -164,7 +166,7 @@ func runDumpRepository(ctx *cli.Context) error {
// the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files
- repoDir := ctx.String("repo_dir")
+ repoDir := cmd.String("repo_dir")
if exists, err := util.IsExist(repoDir); err != nil {
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
} else if exists {
@@ -179,7 +181,7 @@ func runDumpRepository(ctx *cli.Context) error {
if err := migrations.DumpRepository(
context.Background(),
repoDir,
- ctx.String("owner_name"),
+ cmd.String("owner_name"),
opts,
); err != nil {
log.Fatal("Failed to dump repository: %v", err)
diff --git a/cmd/embedded.go b/cmd/embedded.go
index 9f03f7be7c..1908352453 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -19,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdEmbedded represents the available extract sub-command.
@@ -28,7 +29,7 @@ var (
Name: "embedded",
Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdList,
subcmdView,
subcmdExtract,
@@ -100,7 +101,7 @@ type assetFile struct {
path string
}
-func initEmbeddedExtractor(c *cli.Context) error {
+func initEmbeddedExtractor(c *cli.Command) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice())
@@ -115,31 +116,31 @@ func initEmbeddedExtractor(c *cli.Context) error {
return nil
}
-func runList(c *cli.Context) error {
+func runList(_ context.Context, c *cli.Command) error {
if err := runListDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runView(c *cli.Context) error {
+func runView(_ context.Context, c *cli.Command) error {
if err := runViewDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runExtract(c *cli.Context) error {
+func runExtract(_ context.Context, c *cli.Command) error {
if err := runExtractDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runListDo(c *cli.Context) error {
+func runListDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -151,7 +152,7 @@ func runListDo(c *cli.Context) error {
return nil
}
-func runViewDo(c *cli.Context) error {
+func runViewDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -174,7 +175,7 @@ func runViewDo(c *cli.Context) error {
return nil
}
-func runExtractDo(c *cli.Context) error {
+func runExtractDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -216,7 +217,7 @@ func runExtractDo(c *cli.Context) error {
for _, a := range matchedAssetFiles {
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
// Non-fatal error
- fmt.Fprintf(os.Stderr, "%s: %v", a.path, err)
+ _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
}
}
@@ -271,7 +272,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil
}
-func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
+func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true)
if err != nil {
@@ -294,16 +295,14 @@ func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string,
}
}
-func compileCollectPatterns(args []string) ([]glob.Glob, error) {
+func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
if len(args) == 0 {
args = []string{"**"}
}
pat := make([]glob.Glob, len(args))
for i := range args {
- if g, err := glob.Compile(args[i], '/'); err != nil {
- return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err)
- } else { //nolint:revive
- pat[i] = g
+ if pat[i], err = glob.Compile(args[i], '/'); err != nil {
+ return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
}
}
return pat, nil
diff --git a/cmd/generate.go b/cmd/generate.go
index 90b32ecaf0..cf491604ef 100644
--- a/cmd/generate.go
+++ b/cmd/generate.go
@@ -5,13 +5,14 @@
package cmd
import (
+ "context"
"fmt"
"os"
"code.gitea.io/gitea/modules/generate"
"github.com/mattn/go-isatty"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -19,7 +20,7 @@ var (
CmdGenerate = &cli.Command{
Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdSecret,
},
}
@@ -27,7 +28,7 @@ var (
subcmdSecret = &cli.Command{
Name: "secret",
Usage: "Generate a secret token",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
@@ -54,7 +55,7 @@ var (
}
)
-func runGenerateInternalToken(c *cli.Context) error {
+func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
internalToken, err := generate.NewInternalToken()
if err != nil {
return err
@@ -69,7 +70,7 @@ func runGenerateInternalToken(c *cli.Context) error {
return nil
}
-func runGenerateLfsJwtSecret(c *cli.Context) error {
+func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
if err != nil {
return err
@@ -84,7 +85,7 @@ func runGenerateLfsJwtSecret(c *cli.Context) error {
return nil
}
-func runGenerateSecretKey(c *cli.Context) error {
+func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
secretKey, err := generate.NewSecretKey()
if err != nil {
return err
diff --git a/cmd/hook.go b/cmd/hook.go
index 578380ab40..2ce272b411 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -20,11 +20,11 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
const (
- hookBatchSize = 30
+ hookBatchSize = 500
)
var (
@@ -34,7 +34,7 @@ var (
Usage: "(internal) Should only be called by Git",
Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL),
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookPostReceive,
@@ -161,12 +161,10 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil
}
-func runHookPreReceive(c *cli.Context) error {
+func runHookPreReceive(ctx context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
- ctx, cancel := installSignals()
- defer cancel()
setup(ctx, c.Bool("debug"))
@@ -292,7 +290,7 @@ Gitea or set your environment appropriately.`, "")
// runHookUpdate avoid to do heavy operations on update hook because it will be
// invoked for every ref update which does not like pre-receive and post-receive
-func runHookUpdate(c *cli.Context) error {
+func runHookUpdate(_ context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
@@ -309,15 +307,12 @@ func runHookUpdate(c *cli.Context) error {
return nil
}
-func runHookPostReceive(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+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(ctx, "update-server-info").RunStdString(nil); err != nil {
- return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
+ if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil {
+ return fmt.Errorf("failed to call 'git update-server-info': %w", err)
}
// Now if we're an internal don't do anything else
@@ -485,7 +480,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
func pushOptions() map[string]string {
opts := make(map[string]string)
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
- for idx := 0; idx < pushCount; idx++ {
+ for idx := range pushCount {
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
kv := strings.SplitN(opt, "=", 2)
if len(kv) == 2 {
@@ -496,10 +491,7 @@ func pushOptions() map[string]string {
return opts
}
-func runHookProcReceive(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runHookProcReceive(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
@@ -740,7 +732,7 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType)
// read prefix
lengthBytes := make([]byte, 4)
- for i := 0; i < 4; i++ {
+ for i := range 4 {
lengthBytes[i], err = in.ReadByte()
if err != nil {
return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err)
diff --git a/cmd/hook_test.go b/cmd/hook_test.go
index 91f24ff2b4..86cd4834f2 100644
--- a/cmd/hook_test.go
+++ b/cmd/hook_test.go
@@ -6,7 +6,6 @@ package cmd
import (
"bufio"
"bytes"
- "context"
"strings"
"testing"
@@ -15,7 +14,7 @@ import (
func TestPktLine(t *testing.T) {
// test read
- ctx := context.Background()
+ ctx := t.Context()
s := strings.NewReader("0000")
r := bufio.NewReader(s)
result, err := readPktLine(ctx, r, pktLineTypeFlush)
diff --git a/cmd/keys.go b/cmd/keys.go
index 7fdbe16119..8710756a81 100644
--- a/cmd/keys.go
+++ b/cmd/keys.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"strings"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdKeys represents the available keys sub-command
@@ -49,7 +50,7 @@ var CmdKeys = &cli.Command{
},
}
-func runKeys(c *cli.Context) error {
+func runKeys(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
return errors.New("No username provided")
}
@@ -68,9 +69,6 @@ func runKeys(c *cli.Context) error {
return errors.New("No key type and content provided")
}
- ctx, cancel := installSignals()
- defer cancel()
-
setup(ctx, c.Bool("debug"))
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
@@ -78,6 +76,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil {
return extra.Error
}
- _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
+ _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
return nil
}
diff --git a/cmd/mailer.go b/cmd/mailer.go
index 0c5f2c8c8d..72bd8e5601 100644
--- a/cmd/mailer.go
+++ b/cmd/mailer.go
@@ -4,24 +4,18 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-func runSendMail(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runSendMail(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
- if err := argsSet(c, "title"); err != nil {
- return err
- }
-
subject := c.String("title")
confirmSkiped := c.Bool("force")
body := c.String("content")
diff --git a/cmd/main.go b/cmd/main.go
index fd648946ef..3b8a8a9311 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"fmt"
"os"
"strings"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// cmdHelp is our own help subcommand with more information
@@ -22,18 +23,18 @@ func cmdHelp() *cli.Command {
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
- Action: func(c *cli.Context) (err error) {
- lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
+ Action: func(ctx context.Context, c *cli.Command) (err error) {
+ lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea
targetCmdIdx := 0
- if c.Command.Name == "help" {
+ if c.Name == "help" {
targetCmdIdx = 1
}
- if lineage[targetCmdIdx+1].Command != nil {
- err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
+ if lineage[targetCmdIdx] != lineage[targetCmdIdx].Root() {
+ err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1] /* parent cmd */, lineage[targetCmdIdx].Name /* sub cmd */)
} else {
err = cli.ShowAppHelp(c)
}
- _, _ = fmt.Fprintf(c.App.Writer, `
+ _, _ = fmt.Fprintf(c.Root().Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
@@ -74,25 +75,25 @@ func appGlobalFlags() []cli.Flag {
}
}
-func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
- command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
+func prepareSubcommandWithGlobalFlags(command *cli.Command) {
+ command.Flags = append(append([]cli.Flag{}, appGlobalFlags()...), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
- command.Subcommands = append(command.Subcommands, cmdHelp())
+ command.Commands = append(command.Commands, cmdHelp())
}
- for i := range command.Subcommands {
- prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
+ for i := range command.Commands {
+ prepareSubcommandWithGlobalFlags(command.Commands[i])
}
}
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
-func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
- return func(ctx *cli.Context) error {
+func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(context.Context, *cli.Command) error {
+ return func(ctx context.Context, cmd *cli.Command) error {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
- for _, curCtx := range ctx.Lineage() {
+ for _, curCtx := range cmd.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
@@ -104,11 +105,11 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
}
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
- if ctx.Bool("help") || action == nil {
+ if cmd.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
- return cmdHelp().Action(ctx)
+ return cmdHelp().Action(ctx, cmd)
}
- return action(ctx)
+ return action(ctx, cmd)
}
}
@@ -117,14 +118,13 @@ type AppVersion struct {
Extra string
}
-func NewMainApp(appVer AppVersion) *cli.App {
- app := cli.NewApp()
- app.Name = "Gitea"
- app.HelpName = "gitea"
+func NewMainApp(appVer AppVersion) *cli.Command {
+ app := &cli.Command{}
+ app.Name = "gitea" // must be lower-cased because it appears in the "USAGE" section like "gitea doctor [command [command options]]"
app.Usage = "A painless self-hosted Git service"
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = appVer.Version + appVer.Extra
- app.EnableBashCompletion = true
+ app.EnableShellCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
@@ -147,29 +147,33 @@ func NewMainApp(appVer AppVersion) *cli.App {
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{
- CmdCert,
+ cmdCert(),
CmdGenerate,
CmdDocs,
}
+ // TODO: we should eventually drop the default command,
+ // but not sure whether it would break Windows users who used to double-click the EXE to run.
app.DefaultCommand = CmdWeb.Name
- globalFlags := appGlobalFlags()
app.Flags = append(app.Flags, cli.VersionFlag)
- app.Flags = append(app.Flags, globalFlags...)
+ app.Flags = append(app.Flags, appGlobalFlags()...)
app.HideHelp = true // use our own help action to show helps (with more information like default config)
app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig {
- prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
+ prepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
}
app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...)
+ setting.InitGiteaEnvVars()
return app
}
-func RunMainApp(app *cli.App, args ...string) error {
- err := app.Run(args)
+func RunMainApp(app *cli.Command, args ...string) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+ err := app.Run(ctx, args)
if err == nil {
return nil
}
diff --git a/cmd/main_test.go b/cmd/main_test.go
index c182b44019..7dfa87a0ef 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -4,9 +4,10 @@
package cmd
import (
+ "context"
+ "errors"
"fmt"
"io"
- "os"
"path/filepath"
"strings"
"testing"
@@ -16,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestMain(m *testing.M) {
@@ -27,10 +28,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
-func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
+func newTestApp(testCmdAction cli.ActionFunc) *cli.Command {
app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
- prepareSubcommandWithConfig(testCmd, appGlobalFlags())
+ prepareSubcommandWithGlobalFlags(testCmd)
app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name
return app
@@ -42,7 +43,7 @@ type runResult struct {
ExitCode int
}
-func runTestApp(app *cli.App, args ...string) (runResult, error) {
+func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
@@ -65,7 +66,7 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)"
- cli.AppHelpTemplate = "(app help template)"
+ cli.RootCommandHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
@@ -109,70 +110,50 @@ func TestCliCmd(t *testing.T) {
},
}
- app := newTestApp(func(ctx *cli.Context) error {
- _, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
- return nil
- })
- var envBackup []string
- for _, s := range os.Environ() {
- if strings.HasPrefix(s, "GITEA_") && strings.Contains(s, "=") {
- envBackup = append(envBackup, s)
- }
- }
- clearGiteaEnv := func() {
- for _, s := range os.Environ() {
- if strings.HasPrefix(s, "GITEA_") {
- _ = os.Unsetenv(s)
- }
- }
- }
- defer func() {
- clearGiteaEnv()
- for _, s := range envBackup {
- k, v, _ := strings.Cut(s, "=")
- _ = os.Setenv(k, v)
- }
- }()
-
for _, c := range cases {
- clearGiteaEnv()
- for k, v := range c.env {
- _ = os.Setenv(k, v)
- }
- args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
- r, err := runTestApp(app, args...)
- assert.NoError(t, err, c.cmd)
- assert.NotEmpty(t, c.exp, c.cmd)
- assert.Contains(t, r.Stdout, c.exp, c.cmd)
+ t.Run(c.cmd, func(t *testing.T) {
+ app := newTestApp(func(ctx context.Context, cmd *cli.Command) error {
+ _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
+ return nil
+ })
+ for k, v := range c.env {
+ t.Setenv(k, v)
+ }
+ args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
+ r, err := runTestApp(app, args...)
+ assert.NoError(t, err, c.cmd)
+ assert.NotEmpty(t, c.exp, c.cmd)
+ assert.Contains(t, r.Stdout, c.exp, c.cmd)
+ })
}
}
func TestCliCmdError(t *testing.T) {
- app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") })
+ app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Equal(t, "", r.Stdout)
+ assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) })
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode)
- assert.Equal(t, "", r.Stdout)
+ assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return nil })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
- assert.Equal(t, "", r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
+ assert.Empty(t, r.Stdout)
+ assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return nil })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
- assert.Equal(t, "", r.Stdout)
- assert.Equal(t, "", r.Stderr)
+ assert.Empty(t, r.Stdout)
+ assert.Empty(t, r.Stderr)
}
diff --git a/cmd/manager.go b/cmd/manager.go
index bd2da8edc7..f0935ea065 100644
--- a/cmd/manager.go
+++ b/cmd/manager.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"os"
"time"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -18,7 +19,7 @@ var (
Name: "manager",
Usage: "Manage the running gitea process",
Description: "This is a command for managing the running gitea process",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdShutdown,
subcmdRestart,
subcmdReloadTemplates,
@@ -108,46 +109,31 @@ var (
}
)
-func runShutdown(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runShutdown(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Shutdown(ctx)
return handleCliResponseExtra(extra)
}
-func runRestart(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRestart(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Restart(ctx)
return handleCliResponseExtra(extra)
}
-func runReloadTemplates(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runReloadTemplates(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.ReloadTemplates(ctx)
return handleCliResponseExtra(extra)
}
-func runFlushQueues(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runFlushQueues(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
return handleCliResponseExtra(extra)
}
-func runProcesses(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runProcesses(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
return handleCliResponseExtra(extra)
diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go
index c2ae25ec57..ac29e7d3e5 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -60,7 +61,7 @@ var (
subcmdLogging = &cli.Command{
Name: "logging",
Usage: "Adjust logging commands",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
{
Name: "pause",
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
@@ -104,7 +105,7 @@ var (
}, {
Name: "add",
Usage: "Add a logger",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
@@ -118,7 +119,6 @@ var (
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
- Value: true,
},
&cli.Int64Flag{
Name: "max-size",
@@ -129,7 +129,6 @@ var (
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
- Value: true,
},
&cli.IntFlag{
Name: "max-days",
@@ -140,7 +139,6 @@ var (
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
- Value: true,
},
&cli.IntFlag{
Name: "compression-level",
@@ -195,10 +193,7 @@ var (
}
)
-func runRemoveLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRemoveLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
logger := c.String("logger")
if len(logger) == 0 {
@@ -210,10 +205,7 @@ func runRemoveLogger(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
-func runAddConnLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runAddConnLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "conn"
@@ -237,13 +229,10 @@ func runAddConnLogger(c *cli.Context) error {
if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
}
- return commonAddLogger(c, mode, vals)
+ return commonAddLogger(ctx, c, mode, vals)
}
-func runAddFileLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runAddFileLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "file"
@@ -270,10 +259,10 @@ func runAddFileLogger(c *cli.Context) error {
if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level")
}
- return commonAddLogger(c, mode, vals)
+ return commonAddLogger(ctx, c, mode, vals)
}
-func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
+func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String()
}
@@ -300,46 +289,33 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if c.IsSet("writer") {
writer = c.String("writer")
}
- ctx, cancel := installSignals()
- defer cancel()
extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra)
}
-func runPauseLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runPauseLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.PauseLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runResumeLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runResumeLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.ResumeLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runReleaseReopenLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.ReleaseReopenLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runSetLogSQL(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
+func runSetLogSQL(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.SetLogSQL(ctx, !c.Bool("off"))
diff --git a/cmd/migrate.go b/cmd/migrate.go
index 4e4dd45af3..e24dc9e572 100644
--- a/cmd/migrate.go
+++ b/cmd/migrate.go
@@ -7,26 +7,23 @@ import (
"context"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/versioned_migration"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdMigrate represents the available migrate sub-command.
var CmdMigrate = &cli.Command{
Name: "migrate",
Usage: "Migrate the database",
- Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
+ Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`,
Action: runMigrate,
}
-func runMigrate(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runMigrate(ctx context.Context, c *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
@@ -36,7 +33,7 @@ func runMigrate(ctx *cli.Context) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go
index 6ece4bf661..2c63e15f50 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -13,7 +13,6 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/migrations"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -21,8 +20,9 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/services/versioned_migration"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdMigrateStorage represents the available migrate storage sub-command.
@@ -196,7 +196,7 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
- if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
+ if artifact.Status == actions_model.ArtifactStatusExpired {
return nil
}
@@ -213,11 +213,8 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
})
}
-func runMigrateStorage(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
@@ -227,7 +224,7 @@ func runMigrateStorage(ctx *cli.Context) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
@@ -238,51 +235,51 @@ func runMigrateStorage(ctx *cli.Context) error {
var dstStorage storage.ObjectStorage
var err error
- switch strings.ToLower(ctx.String("storage")) {
+ switch strings.ToLower(cmd.String("storage")) {
case "":
fallthrough
case string(setting.LocalStorageType):
- p := ctx.String("path")
+ p := cmd.String("path")
if p == "" {
log.Fatal("Path must be given when storage is local")
return nil
}
dstStorage, err = storage.NewLocalStorage(
- stdCtx,
+ ctx,
&setting.Storage{
Path: p,
})
case string(setting.MinioStorageType):
dstStorage, err = storage.NewMinioStorage(
- stdCtx,
+ ctx,
&setting.Storage{
MinioConfig: setting.MinioStorageConfig{
- Endpoint: ctx.String("minio-endpoint"),
- AccessKeyID: ctx.String("minio-access-key-id"),
- SecretAccessKey: ctx.String("minio-secret-access-key"),
- Bucket: ctx.String("minio-bucket"),
- Location: ctx.String("minio-location"),
- BasePath: ctx.String("minio-base-path"),
- UseSSL: ctx.Bool("minio-use-ssl"),
- InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
- ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
- BucketLookUpType: ctx.String("minio-bucket-lookup-type"),
+ Endpoint: cmd.String("minio-endpoint"),
+ AccessKeyID: cmd.String("minio-access-key-id"),
+ SecretAccessKey: cmd.String("minio-secret-access-key"),
+ Bucket: cmd.String("minio-bucket"),
+ Location: cmd.String("minio-location"),
+ BasePath: cmd.String("minio-base-path"),
+ UseSSL: cmd.Bool("minio-use-ssl"),
+ InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"),
+ ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"),
+ BucketLookUpType: cmd.String("minio-bucket-lookup-type"),
},
})
case string(setting.AzureBlobStorageType):
dstStorage, err = storage.NewAzureBlobStorage(
- stdCtx,
+ ctx,
&setting.Storage{
AzureBlobConfig: setting.AzureBlobStorageConfig{
- Endpoint: ctx.String("azureblob-endpoint"),
- AccountName: ctx.String("azureblob-account-name"),
- AccountKey: ctx.String("azureblob-account-key"),
- Container: ctx.String("azureblob-container"),
- BasePath: ctx.String("azureblob-base-path"),
+ Endpoint: cmd.String("azureblob-endpoint"),
+ AccountName: cmd.String("azureblob-account-name"),
+ AccountKey: cmd.String("azureblob-account-key"),
+ Container: cmd.String("azureblob-container"),
+ BasePath: cmd.String("azureblob-base-path"),
},
})
default:
- return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
+ return fmt.Errorf("unsupported storage type: %s", cmd.String("storage"))
}
if err != nil {
return err
@@ -299,14 +296,14 @@ func runMigrateStorage(ctx *cli.Context) error {
"actions-artifacts": migrateActionsArtifacts,
}
- tp := strings.ToLower(ctx.String("type"))
+ tp := strings.ToLower(cmd.String("type"))
if m, ok := migratedMethods[tp]; ok {
- if err := m(stdCtx, dstStorage); err != nil {
+ if err := m(ctx, dstStorage); err != nil {
return err
}
log.Info("%s files have successfully been copied to the new storage.", tp)
return nil
}
- return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
+ return fmt.Errorf("unsupported storage: %s", cmd.String("type"))
}
diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go
index 5d8c867993..6817867e28 100644
--- a/cmd/migrate_storage_test.go
+++ b/cmd/migrate_storage_test.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"os"
"strings"
"testing"
@@ -53,7 +52,7 @@ func TestMigratePackages(t *testing.T) {
assert.NotNil(t, v)
assert.NotNil(t, f)
- ctx := context.Background()
+ ctx := t.Context()
p := t.TempDir()
@@ -70,6 +69,6 @@ func TestMigratePackages(t *testing.T) {
entries, err := os.ReadDir(p)
assert.NoError(t, err)
assert.Len(t, entries, 2)
- assert.EqualValues(t, "01", entries[0].Name())
- assert.EqualValues(t, "tmp", entries[1].Name())
+ assert.Equal(t, "01", entries[0].Name())
+ assert.Equal(t, "tmp", entries[1].Name())
}
diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go
index 37b32aa304..c61f5a582e 100644
--- a/cmd/restore_repo.go
+++ b/cmd/restore_repo.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"strings"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdRestoreRepository represents the available restore a repository sub-command.
@@ -48,10 +49,7 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runRestoreRepository(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRestoreRepository(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
var units []string
if s := c.String("units"); s != "" {
diff --git a/cmd/serv.go b/cmd/serv.go
index d2271b68d2..8c6001e727 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -11,7 +11,6 @@ import (
"os"
"os/exec"
"path/filepath"
- "regexp"
"strconv"
"strings"
"time"
@@ -20,7 +19,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
@@ -34,15 +33,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote"
- "github.com/urfave/cli/v2"
-)
-
-const (
- verbUploadPack = "git-upload-pack"
- verbUploadArchive = "git-upload-archive"
- verbReceivePack = "git-receive-pack"
- verbLfsAuthenticate = "git-lfs-authenticate"
- verbLfsTransfer = "git-lfs-transfer"
+ "github.com/urfave/cli/v3"
)
// CmdServ represents the available serv sub-command.
@@ -78,22 +69,6 @@ func setup(ctx context.Context, debug bool) {
}
}
-var (
- // keep getAccessMode() in sync
- allowedCommands = container.SetOf(
- verbUploadPack,
- verbUploadArchive,
- verbReceivePack,
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- allowedCommandsLfs = container.SetOf(
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
-)
-
// fail prints message to stdout, it's mainly used for git serv and git hook commands.
// The output will be passed to git client and shown to user.
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
@@ -104,7 +79,10 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
// There appears to be a chance to cause a zombie process and failure to read the Exit status
// if nothing is outputted on stdout.
_, _ = fmt.Fprintln(os.Stdout, "")
- _, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
+ // add extra empty lines to separate our message from other git errors to get more attention
+ _, _ = fmt.Fprintln(os.Stderr, "error:")
+ _, _ = fmt.Fprintln(os.Stderr, "error:", userMessage)
+ _, _ = fmt.Fprintln(os.Stderr, "error:")
if logMsgFmt != "" {
logMsg := fmt.Sprintf(logMsgFmt, args...)
@@ -136,19 +114,20 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
switch verb {
- case verbUploadPack, verbUploadArchive:
+ case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
return perm.AccessModeRead
- case verbReceivePack:
+ case git.CmdVerbReceivePack:
return perm.AccessModeWrite
- case verbLfsAuthenticate, verbLfsTransfer:
+ case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
switch lfsVerb {
- case "upload":
+ case git.CmdSubVerbLfsUpload:
return perm.AccessModeWrite
- case "download":
+ case git.CmdSubVerbLfsDownload:
return perm.AccessModeRead
}
}
// should be unreachable
+ setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
@@ -170,13 +149,10 @@ func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServC
if err != nil {
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
}
- return fmt.Sprintf("Bearer %s", tokenString), nil
+ return "Bearer " + tokenString, nil
}
-func runServ(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runServ(ctx context.Context, c *cli.Command) error {
// FIXME: This needs to internationalised
setup(ctx, c.Bool("debug"))
@@ -227,41 +203,37 @@ func runServ(c *cli.Context) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
}
- words, err := shellquote.Split(cmd)
+ sshCmdArgs, err := shellquote.Split(cmd)
if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
}
- if len(words) < 2 {
+ if len(sshCmdArgs) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
- fmt.Print(`{"type":"gitea","version":1}`)
+ fmt.Print(`{"type":"agit","version":1}`)
return nil
}
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
- verb := words[0]
- repoPath := strings.TrimPrefix(words[1], "/")
-
- var lfsVerb string
-
- rr := strings.SplitN(repoPath, "/", 2)
- if len(rr) != 2 {
+ repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
+ repoPathFields := strings.SplitN(repoPath, "/", 2)
+ if len(repoPathFields) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
}
- username := rr[0]
- reponame := strings.TrimSuffix(rr[1], ".git")
+ 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 alphaDashDotPattern.MatchString(reponame) {
+ if !repo.IsValidSSHAccessRepoName(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
}
@@ -283,22 +255,23 @@ func runServ(c *cli.Context) error {
}()
}
- if allowedCommands.Contains(verb) {
- if allowedCommandsLfs.Contains(verb) {
- if !setting.LFS.StartServer {
- return fail(ctx, "LFS Server is not enabled", "")
- }
- if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
- return fail(ctx, "LFS SSH transfer is not enabled", "")
- }
- if len(words) > 2 {
- lfsVerb = words[2]
- }
- }
- } else {
+ verb, lfsVerb := sshCmdArgs[0], ""
+ if !git.IsAllowedVerbForServe(verb) {
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
}
+ if git.IsAllowedVerbForServeLfs(verb) {
+ if !setting.LFS.StartServer {
+ return fail(ctx, "LFS Server is not enabled", "")
+ }
+ if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH {
+ return fail(ctx, "LFS SSH transfer is not enabled", "")
+ }
+ if len(sshCmdArgs) > 2 {
+ lfsVerb = sshCmdArgs[2]
+ }
+ }
+
requestedMode := getAccessMode(verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
@@ -307,7 +280,7 @@ func runServ(c *cli.Context) error {
}
// LFS SSH protocol
- if verb == verbLfsTransfer {
+ if verb == git.CmdVerbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
@@ -316,7 +289,7 @@ func runServ(c *cli.Context) error {
}
// LFS token authentication
- if verb == verbLfsAuthenticate {
+ if verb == git.CmdVerbLfsAuthenticate {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
token, err := getLFSAuthToken(ctx, lfsVerb, results)
@@ -369,9 +342,9 @@ func runServ(c *cli.Context) error {
repo_module.EnvPusherEmail+"="+results.UserEmail,
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
- repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
- repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
- repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
+ repo_module.EnvPRID+"="+strconv.Itoa(0),
+ repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10),
+ repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10),
repo_module.EnvAppURL+"="+setting.AppURL,
)
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
diff --git a/cmd/web.go b/cmd/web.go
index ef8a7426c1..61ee3cbc20 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -12,20 +12,23 @@ import (
"path/filepath"
"strconv"
"strings"
+ "time"
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/install"
"github.com/felixge/fgprof"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// PIDFile could be set from build tag
@@ -115,21 +118,31 @@ func showWebStartupMessage(msg string) {
log.Info("* CustomPath: %s", setting.CustomPath)
log.Info("* ConfigFile: %s", setting.CustomConf)
log.Info("%s", msg) // show startup message
+
+ if setting.CORSConfig.Enabled {
+ log.Info("CORS Service Enabled")
+ }
+ if setting.DefaultUILocation != time.Local {
+ log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
+ }
+ if setting.MailService != nil {
+ log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
+ }
}
-func serveInstall(ctx *cli.Context) error {
+func serveInstall(cmd *cli.Command) error {
showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
// Flag for port number in case first time run conflict
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
+ if cmd.IsSet("port") {
+ if err := setPort(cmd.String("port")); err != nil {
return err
}
}
- if ctx.IsSet("install-port") {
- if err := setPort(ctx.String("install-port")); err != nil {
+ if cmd.IsSet("install-port") {
+ if err := setPort(cmd.String("install-port")); err != nil {
return err
}
}
@@ -150,7 +163,7 @@ func serveInstall(ctx *cli.Context) error {
return nil
}
-func serveInstalled(ctx *cli.Context) error {
+func serveInstalled(c *cli.Command) error {
setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings()
setting.MustInstalled()
@@ -200,13 +213,19 @@ func serveInstalled(ctx *cli.Context) error {
log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
}
+ // the AppDataTempDir is fully managed by us with a safe sub-path
+ // so it's safe to automatically remove the outdated files
+ setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
+
// Override the provided port number within the configuration
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
+ if c.IsSet("port") {
+ if err := setPort(c.String("port")); err != nil {
return err
}
}
+ gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
+
// Set up Chi routes
webRoutes := routers.NormalRoutes()
err := listen(webRoutes, true)
@@ -225,13 +244,17 @@ func servePprof() {
finished()
}
-func runWeb(ctx *cli.Context) error {
+func runWeb(_ context.Context, cmd *cli.Command) error {
defer func() {
if panicked := recover(); panicked != nil {
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
}
}()
+ if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid {
+ return fmt.Errorf("unknown command: %s", subCmdName)
+ }
+
managerCtx, cancel := context.WithCancel(context.Background())
graceful.InitManager(managerCtx)
defer cancel()
@@ -243,12 +266,12 @@ func runWeb(ctx *cli.Context) error {
}
// Set pid file setting
- if ctx.IsSet("pid") {
- createPIDFile(ctx.String("pid"))
+ if cmd.IsSet("pid") {
+ createPIDFile(cmd.String("pid"))
}
if !setting.InstallLock {
- if err := serveInstall(ctx); err != nil {
+ if err := serveInstall(cmd); err != nil {
return err
}
} else {
@@ -259,7 +282,7 @@ func runWeb(ctx *cli.Context) error {
go servePprof()
}
- return serveInstalled(ctx)
+ return serveInstalled(cmd)
}
func setPort(port string) error {
diff --git a/cmd/web_acme.go b/cmd/web_acme.go
index 90e4a02764..5f7a308334 100644
--- a/cmd/web_acme.go
+++ b/cmd/web_acme.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/caddyserver/certmagic"
)
@@ -54,8 +55,6 @@ func runACME(listenAddr string, m http.Handler) error {
altTLSALPNPort = p
}
- magic := certmagic.NewDefault()
- magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
// Try to use private CA root if provided, otherwise defaults to system's trust
var certPool *x509.CertPool
if setting.AcmeCARoot != "" {
@@ -65,8 +64,20 @@ func runACME(listenAddr string, m http.Handler) error {
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
}
}
- myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
- CA: setting.AcmeURL,
+ // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https"
+ // Ideally it should migrate to AppDataPath write to "AppDataPath/https"
+ // And one more thing, no idea why we should set the global default variables here
+ // But it seems that the current ACME code needs these global variables to make renew work.
+ // Otherwise, "renew" will use incorrect storage path
+ oldDefaultACME := certmagic.DefaultACME
+ certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
+ certmagic.DefaultACME = certmagic.ACMEIssuer{
+ // try to use the default values provided by DefaultACME
+ CA: util.IfZero(setting.AcmeURL, oldDefaultACME.CA),
+ TestCA: oldDefaultACME.TestCA,
+ Logger: oldDefaultACME.Logger,
+ HTTPProxy: oldDefaultACME.HTTPProxy,
+
TrustedRoots: certPool,
Email: setting.AcmeEmail,
Agreed: setting.AcmeTOS,
@@ -75,8 +86,10 @@ func runACME(listenAddr string, m http.Handler) error {
ListenHost: setting.HTTPAddr,
AltTLSALPNPort: altTLSALPNPort,
AltHTTPPort: altHTTPPort,
- })
+ }
+ magic := certmagic.NewDefault()
+ myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME)
magic.Issuers = []certmagic.Issuer{myACME}
// this obtains certificates or renews them if necessary
@@ -123,7 +136,7 @@ func runACME(listenAddr string, m http.Handler) error {
}
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != "GET" && r.Method != "HEAD" {
+ if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go
index 996537be3b..5e06d2c216 100644
--- a/cmd/web_graceful.go
+++ b/cmd/web_graceful.go
@@ -23,12 +23,6 @@ func NoHTTPRedirector() {
graceful.GetManager().InformCleanup()
}
-// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
-// for our main HTTP/HTTPS service
-func NoMainListener() {
- graceful.GetManager().InformCleanup()
-}
-
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
// for our install HTTP/HTTPS service
func NoInstallListener() {
diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go
index eb19437445..2052295fb1 100644
--- a/contrib/backport/backport.go
+++ b/contrib/backport/backport.go
@@ -1,31 +1,30 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
+//nolint:forbidigo // use of print functions is allowed in cli
package main
import (
"context"
+ "errors"
"fmt"
"log"
"net/http"
"os"
"os/exec"
- "os/signal"
"path"
"strconv"
"strings"
- "syscall"
- "github.com/google/go-github/v61/github"
- "github.com/urfave/cli/v2"
+ "github.com/google/go-github/v71/github"
+ "github.com/urfave/cli/v3"
"gopkg.in/yaml.v3"
)
const defaultVersion = "v1.18" // to backport to
func main() {
- app := cli.NewApp()
+ app := &cli.Command{}
app.Name = "backport"
app.Usage = "Backport provided PR-number on to the current or previous released version"
app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
@@ -90,7 +89,7 @@ func main() {
Usage: "Set this flag to continue from a git cherry-pick that has broken",
},
}
- cli.AppHelpTemplate = `NAME:
+ cli.RootCommandHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
@@ -104,16 +103,12 @@ OPTIONS:
`
app.Action = runBackport
-
- if err := app.Run(os.Args); err != nil {
+ if err := app.Run(context.Background(), os.Args); err != nil {
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
}
}
-func runBackport(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runBackport(ctx context.Context, c *cli.Command) error {
continuing := c.Bool("continue")
var pr string
@@ -158,7 +153,7 @@ func runBackport(c *cli.Context) error {
args := c.Args().Slice()
if len(args) == 0 && pr == "" {
- return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
+ return errors.New("no PR number provided\nProvide a PR number to backport")
} else if len(args) != 1 && pr == "" {
return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args)
}
@@ -342,8 +337,8 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
}
- lines := strings.Split(string(out), "\n")
- for _, line := range lines {
+ lines := strings.SplitSeq(string(out), "\n")
+ for line := range lines {
fields := strings.Split(line, "\t")
name, remote := fields[0], fields[1]
// only look at pushers
@@ -361,12 +356,12 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
if !strings.Contains(remote, forkUser) {
continue
}
- if strings.HasPrefix(remote, "git@github.com:") {
- forkUser = strings.TrimPrefix(remote, "git@github.com:")
- } else if strings.HasPrefix(remote, "https://github.com/") {
- forkUser = strings.TrimPrefix(remote, "https://github.com/")
- } else if strings.HasPrefix(remote, "https://www.github.com/") {
- forkUser = strings.TrimPrefix(remote, "https://www.github.com/")
+ if after, ok := strings.CutPrefix(remote, "git@github.com:"); ok {
+ forkUser = after
+ } else if after, ok := strings.CutPrefix(remote, "https://github.com/"); ok {
+ forkUser = after
+ } else if after, ok := strings.CutPrefix(remote, "https://www.github.com/"); ok {
+ forkUser = after
} else if forkUser == "" {
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
}
@@ -459,25 +454,3 @@ func determineSHAforPR(ctx context.Context, prStr, accessToken string) (string,
return "", nil
}
-
-func installSignals() (context.Context, context.CancelFunc) {
- ctx, cancel := context.WithCancel(context.Background())
- go func() {
- // install notify
- signalChannel := make(chan os.Signal, 1)
-
- signal.Notify(
- signalChannel,
- syscall.SIGINT,
- syscall.SIGTERM,
- )
- select {
- case <-signalChannel:
- case <-ctx.Done():
- }
- cancel()
- signal.Reset()
- }()
-
- return ctx, cancel
-}
diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go
index a7d7a6d293..5eb576c6fe 100644
--- a/contrib/environment-to-ini/environment-to-ini.go
+++ b/contrib/environment-to-ini/environment-to-ini.go
@@ -4,16 +4,17 @@
package main
import (
+ "context"
"os"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func main() {
- app := cli.NewApp()
+ app := cli.Command{}
app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the gitea configuration
@@ -72,13 +73,13 @@ func main() {
},
}
app.Action = runEnvironmentToIni
- err := app.Run(os.Args)
+ err := app.Run(context.Background(), os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
}
}
-func runEnvironmentToIni(c *cli.Context) error {
+func runEnvironmentToIni(_ context.Context, c *cli.Command) error {
// the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...)
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 6377ebf9d2..aa2fcee765 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -59,27 +59,22 @@ RUN_USER = ; git
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; The protocol the server listens on. One of 'http', 'https', 'http+unix', 'fcgi' or 'fcgi+unix'. Defaults to 'http'
-;; Note: Value must be lowercase.
+;; The protocol the server listens on. One of "http", "https", "http+unix", "fcgi" or "fcgi+unix".
;PROTOCOL = http
;;
-;; Expect PROXY protocol headers on connections
-;USE_PROXY_PROTOCOL = false
-;;
-;; Use PROXY protocol in TLS Bridging mode
-;PROXY_PROTOCOL_TLS_BRIDGING = false
-;;
-; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
-;PROXY_PROTOCOL_HEADER_TIMEOUT=5s
-;;
-; Accept PROXY protocol headers with UNKNOWN type
-;PROXY_PROTOCOL_ACCEPT_UNKNOWN=false
-;;
-;; Set the domain for the server
+;; Set the domain for the server.
;DOMAIN = localhost
;;
-;; Overwrite the automatically generated public URL. Necessary for proxies and docker.
-;ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
+;; The AppURL is used to generate public URL links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
+;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy.
+;ROOT_URL =
+;;
+;; Controls how to detect the public URL.
+;; Although it defaults to "legacy" (to avoid breaking existing users), most instances should use the "auto" behavior,
+;; especially when the Gitea instance needs to be accessed in a container network.
+;; * legacy: detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL".
+;; * auto: always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL".
+;PUBLIC_URL_DETECTION = legacy
;;
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
;; DO NOT USE IT IN PRODUCTION!!!
@@ -89,13 +84,25 @@ RUN_USER = ; git
;STATIC_URL_PREFIX =
;;
;; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
-;; If PROTOCOL is set to `http+unix` or `fcgi+unix`, this should be the name of the Unix socket file to use.
+;; If PROTOCOL is set to "http+unix" or "fcgi+unix", this should be the name of the Unix socket file to use.
;; Relative paths will be made absolute against the _`AppWorkPath`_.
;HTTP_ADDR = 0.0.0.0
;;
-;; The port to listen on. Leave empty when using a unix socket.
+;; The port to listen on for "http" or "https" protocol. Leave empty when using a unix socket.
;HTTP_PORT = 3000
;;
+;; Expect PROXY protocol headers on connections
+;USE_PROXY_PROTOCOL = false
+;;
+;; Use PROXY protocol in TLS Bridging mode
+;PROXY_PROTOCOL_TLS_BRIDGING = false
+;;
+;; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
+;PROXY_PROTOCOL_HEADER_TIMEOUT = 5s
+;;
+;; Accept PROXY protocol headers with UNKNOWN type
+;PROXY_PROTOCOL_ACCEPT_UNKNOWN = false
+;;
;; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
;; will be started on PORT_TO_REDIRECT and it will redirect plain, non-secure http requests to the main
;; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
@@ -103,8 +110,8 @@ RUN_USER = ; git
;REDIRECT_OTHER_PORT = false
;PORT_TO_REDIRECT = 80
;;
-;; expect PROXY protocol header on connections to https redirector.
-;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
+;; expect PROXY protocol header on connections to https redirector, defaults to USE_PROXY_PROTOCOL
+;REDIRECTOR_USE_PROXY_PROTOCOL =
;; Minimum and maximum supported TLS versions
;SSL_MIN_VERSION=TLSv1.2
;SSL_MAX_VERSION=
@@ -128,13 +135,14 @@ RUN_USER = ; git
;; most cases you do not need to change the default value. Alter it only if
;; your SSH server node is not the same as HTTP node. For different protocol, the default
;; values are different. If `PROTOCOL` is `http+unix`, the default value is `http://unix/`.
-;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
-;; If listen on `0.0.0.0`, the default value is `%(PROTOCOL)s://localhost:%(HTTP_PORT)s/`, Otherwise the default
-;; value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
-;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
+;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
+;; If listen on `0.0.0.0`, the default value is `{PROTOCOL}://localhost:{HTTP_PORT}/`.
+;; Otherwise the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
+;; Most users don't need (and shouldn't) set this value.
+;LOCAL_ROOT_URL =
;;
-;; When making local connections pass the PROXY protocol header.
-;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
+;; When making local connections pass the PROXY protocol header, defaults to USE_PROXY_PROTOCOL
+;LOCAL_USE_PROXY_PROTOCOL =
;;
;; Disable SSH feature when not available
;DISABLE_SSH = false
@@ -146,13 +154,17 @@ RUN_USER = ; git
;SSH_SERVER_USE_PROXY_PROTOCOL = false
;;
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
-;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
+;BUILTIN_SSH_SERVER_USER =
;;
-;; Domain name to be exposed in clone URL
-;SSH_DOMAIN = %(DOMAIN)s
+;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
+;SSH_DOMAIN =
;;
-;; SSH username displayed in clone URLs.
-;SSH_USER = %(BUILTIN_SSH_SERVER_USER)s
+;; SSH username displayed in clone URLs. It defaults to BUILTIN_SSH_SERVER_USER or RUN_USER.
+;; If it is set to "(DOER_USERNAME)", it will use current signed-in user's username.
+;; This option is only for some advanced users who have configured their SSH reverse-proxy
+;; and need to use different usernames for git SSH clone.
+;; Most users should just leave it blank.
+;SSH_USER =
;;
;; The network interface the builtin SSH server should listen on
;SSH_LISTEN_HOST =
@@ -160,8 +172,8 @@ RUN_USER = ; git
;; Port number to be exposed in clone URL
;SSH_PORT = 22
;;
-;; The port number the builtin SSH server should listen on
-;SSH_LISTEN_PORT = %(SSH_PORT)s
+;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
+;SSH_LISTEN_PORT =
;;
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
;SSH_ROOT_PATH =
@@ -174,30 +186,19 @@ RUN_USER = ; git
;; If you intend to use the AuthorizedPrincipalsCommand functionality then you should turn this off.
;SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE = true
;;
-;; For the built-in SSH server, choose the ciphers to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_CIPHERS = chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com
-;;
-;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_KEY_EXCHANGES = curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
-;;
-;; For the built-in SSH server, choose the MACs to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1
+;; For the builtin SSH server, choose the supported ciphers/key-exchange-algorithms/MACs for SSH connections.
+;; The supported names are listed in https://github.com/golang/crypto/blob/master/ssh/common.go.
+;; Leave them empty to use the Golang crypto's recommended default values.
+;; For system SSH (non-builtin SSH server), this setting has no effect.
+;SSH_SERVER_CIPHERS =
+;SSH_SERVER_KEY_EXCHANGES =
+;SSH_SERVER_MACS =
;;
;; For the built-in SSH server, choose the keypair to offer as the host key
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
-;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
+;; relative paths are made absolute relative to the APP_DATA_PATH
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
;;
-;; Directory to create temporary files in when testing public keys using ssh-keygen,
-;; default is the system temporary directory.
-;SSH_KEY_TEST_PATH =
-;;
-;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
-;SSH_KEYGEN_PATH =
-;;
;; Enable SSH Authorized Key Backup when rewriting all keys, default is false
;SSH_AUTHORIZED_KEYS_BACKUP = false
;;
@@ -288,6 +289,9 @@ RUN_USER = ; git
;; Default path for App data
;APP_DATA_PATH = data ; relative paths will be made absolute with _`AppWorkPath`_
;;
+;; Base path for App's temp files, leave empty to use the managed tmp directory in APP_DATA_PATH
+;APP_TEMP_PATH =
+;;
;; Enable gzip compression for runtime-generated content, static resources excluded
;ENABLE_GZIP = false
;;
@@ -516,6 +520,10 @@ INTERNAL_TOKEN =
;;
;; On user registration, record the IP address and user agent of the user to help identify potential abuse.
;; RECORD_USER_SIGNUP_METADATA = false
+;;
+;; Set the two-factor auth behavior.
+;; Set to "enforced", to force users to enroll into Two-Factor Authentication, users without 2FA have no access to repositories via API or web.
+;TWO_FACTOR_AUTH =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -582,7 +590,7 @@ ENABLED = true
[log]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Root path for the log files - defaults to %(GITEA_WORK_DIR)/log
+;; Root path for the log files - defaults to "{AppWorkPath}/log"
;ROOT_PATH =
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -682,8 +690,8 @@ LEVEL = Info
;; The path of git executable. If empty, Gitea searches through the PATH environment.
;PATH =
;;
-;; The HOME directory for Git
-;HOME_PATH = %(APP_DATA_PATH)s/home
+;; The HOME directory for Git, defaults to "{APP_DATA_PATH}/home"
+;HOME_PATH =
;;
;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false
@@ -774,6 +782,9 @@ LEVEL = Info
;ALLOW_ONLY_EXTERNAL_REGISTRATION = false
;;
;; User must sign in to view anything.
+;; It could be set to "expensive" to block anonymous users accessing some pages which consume a lot of resources,
+;; for example: block anonymous AI crawlers from accessing repo code pages.
+;; The "expensive" mode is experimental and subject to change.
;REQUIRE_SIGNIN_VIEW = false
;;
;; Mail notification
@@ -784,10 +795,13 @@ LEVEL = Info
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
;ENABLE_BASIC_AUTHENTICATION = true
;;
-;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods.
+;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 or passkey login methods if they are enabled.
;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication.
;ENABLE_PASSWORD_SIGNIN_FORM = true
;;
+;; Allow users to sign-in with a passkey
+;ENABLE_PASSKEY_AUTHENTICATION = true
+;;
;; More detail: https://github.com/gogits/gogs/issues/165
;ENABLE_REVERSE_PROXY_AUTHENTICATION = false
; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible.
@@ -932,7 +946,29 @@ LEVEL = Info
;;
;; Disable the code explore page.
;DISABLE_CODE_PAGE = false
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;[qos]
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
+;; Enable request quality of service and overload protection.
+; ENABLED = false
+;;
+;; The maximum number of concurrent requests that the server will
+;; process before enqueueing new requests. Default is "CpuNum * 4".
+; MAX_INFLIGHT =
+;;
+;; The maximum number of requests that can be enqueued before new
+;; requests will be dropped.
+; MAX_WAITING = 100
+;;
+;; Target maximum wait time a request may be enqueued for. Requests
+;; that are enqueued for less than this amount of time will not be
+;; dropped. When wait times exceed this amount, a portion of requests
+;; will be dropped until wait times have decreased below this amount.
+; TARGET_WAIT_TIME = 250ms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -946,8 +982,8 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)s/gitea-repositories.
-;; A relative path is interpreted as _`AppWorkPath`_/%(ROOT)s
+;; Root path for storing all repository data. By default, it is set to "{APP_DATA_PATH}/gitea-repositories".
+;; A relative path is interpreted as "{AppWorkPath}/{ROOT}" (use AppWorkPath as base path).
;ROOT =
;;
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
@@ -1057,15 +1093,6 @@ LEVEL = Info
;; Separate extensions with a comma. To line wrap files without an extension, just put a comma
;LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,.livemd,
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;[repository.local]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Path for local repository copy. Defaults to `tmp/local-repo` (content gets deleted on gitea restart)
-;LOCAL_COPY_PATH = tmp/local-repo
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.upload]
@@ -1075,9 +1102,6 @@ LEVEL = Info
;; Whether repository file uploads are enabled. Defaults to `true`
;ENABLED = true
;;
-;; Path for uploads. Defaults to `data/tmp/uploads` (content gets deleted on gitea restart)
-;TEMP_PATH = data/tmp/uploads
-;;
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
;ALLOWED_TYPES =
;;
@@ -1120,6 +1144,9 @@ LEVEL = Info
;; In default merge messages only include approvers who are official
;DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY = true
;;
+;; In default squash-merge messages include the commit message of all commits comprising the pull request.
+;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
+;;
;; Add co-authored-by and co-committed-by trailers if committer does not match author
;ADD_CO_COMMITTER_TRAILERS = true
;;
@@ -1128,6 +1155,10 @@ LEVEL = Info
;;
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
;RETARGET_CHILDREN_ON_MERGE = true
+;;
+;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated.
+;; Use "-1" to always check all pull requests (old behavior). Use "0" to always delay the checks.
+;DELAY_CHECK_FOR_INACTIVE_DAYS = 7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1155,17 +1186,24 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
+;; GPG or SSH key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
+;; Depending on the value of SIGNING_FORMAT this is either:
+;; - openpgp: the GPG key ID
+;; - ssh: the path to the ssh public key "/path/to/key.pub": where "/path/to/key" is the private key, use ssh-keygen -t ed25519 to generate a new key pair without password
;; run in the context of the RUN_USER
;; Switch to none to stop signing completely
;SIGNING_KEY = default
;;
-;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
+;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer and the signing format.
;; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
-;; the results of git config --get user.name and git config --get user.email respectively and can only be overridden
+;; the results of git config --get user.name, git config --get user.email and git config --default openpgp --get gpg.format respectively and can only be overridden
;; by setting the SIGNING_KEY ID to the correct ID.)
;SIGNING_NAME =
;SIGNING_EMAIL =
+;; SIGNING_FORMAT can be one of:
+;; - openpgp (default): use GPG to sign commits
+;; - ssh: use SSH to sign commits
+;SIGNING_FORMAT = openpgp
;;
;; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter
;DEFAULT_TRUST_MODEL = collaborator
@@ -1192,6 +1230,13 @@ LEVEL = Info
;; - commitssigned: require that all the commits in the head branch are signed.
;; - approved: only sign when merging an approved pr to a protected branch
;MERGES = pubkey, twofa, basesigned, commitssigned
+;;
+;; Determines which additional ssh keys are trusted for all signed commits regardless of the user
+;; This is useful for ssh signing key rotation.
+;; Exposes the provided SIGNING_NAME and SIGNING_EMAIL as the signer, regardless of the SIGNING_FORMAT value.
+;; Multiple keys should be comma separated.
+;; E.g."ssh- ". or "ssh- , ssh- ".
+;TRUSTED_SSH_KEYS =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1282,6 +1327,9 @@ LEVEL = Info
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
;THEMES =
;;
+;; The icons for file list (basic/material), this is a temporary option which will be replaced by a user setting in the future.
+;FILE_ICON_THEME = material
+;;
;; All available reactions users can choose on issues/prs and comments.
;; Values can be emoji alias (:smile:) or a unicode emoji.
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
@@ -1339,6 +1387,9 @@ LEVEL = Info
;; Number of repos that are displayed on one page
;REPO_PAGING_NUM = 15
+;; Number of orgs that are displayed on profile page
+;ORG_PAGING_NUM = 15
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[ui.meta]
@@ -1392,14 +1443,14 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; Render soft line breaks as hard line breaks, which means a single newline character between
-;; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
-;; necessary to force a line break.
-;; Render soft line breaks as hard line breaks for comments
-;ENABLE_HARD_LINE_BREAK_IN_COMMENTS = true
-;;
-;; Render soft line breaks as hard line breaks for markdown documents
-;ENABLE_HARD_LINE_BREAK_IN_DOCUMENTS = false
+;; Customize render options for different contexts. Set to "none" to disable the defaults, or use comma separated list:
+;; * short-issue-pattern: recognized "#123" issue reference and render it as a link to the issue
+;; * new-line-hard-break: render soft line breaks as hard line breaks, which means a single newline character between
+;; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
+;; necessary to force a line break.
+;RENDER_OPTIONS_COMMENT = short-issue-pattern, new-line-hard-break
+;RENDER_OPTIONS_WIKI = short-issue-pattern
+;RENDER_OPTIONS_REPO_FILE =
;;
;; Comma separated list of custom URL-Schemes that are allowed as links when rendering Markdown
;; for example git,magnet,ftp (more at https://en.wikipedia.org/wiki/List_of_URI_schemes)
@@ -1413,6 +1464,11 @@ LEVEL = Info
;;
;; Enables math inline and block detection
;ENABLE_MATH = true
+;;
+;; Enable delimiters for math code block detection. Set to "none" to disable all,
+;; or use comma separated list: inline-dollar, inline-parentheses, block-dollar, block-square-brackets
+;; Defaults to "inline-dollar,block-dollar" to follow GitHub's behavior.
+;MATH_CODE_BLOCK_DETECTION =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1482,6 +1538,10 @@ LEVEL = Info
;REPO_INDEXER_EXCLUDE =
;;
;MAX_FILE_SIZE = 1048576
+;;
+;; Bleve engine has performance problems with fuzzy search, so we limit the fuzziness to 0 by default to disable it.
+;; If you'd like to enable it, you can set it to a value between 0 and 2.
+;TYPE_BLEVE_MAX_FUZZINESS = 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1499,7 +1559,8 @@ LEVEL = Info
;TYPE = persistable-channel
;;
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
-;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
+;; Relative paths will be made absolute against "APP_DATA_PATH"
+;DATADIR = queues/
;;
;; Default queue length before a channel queue will block
;LENGTH = 100000
@@ -1747,6 +1808,9 @@ LEVEL = Info
;;
;; convert \r\n to \n for Sendmail
;SENDMAIL_CONVERT_CRLF = true
+;;
+;; convert links of attached images to inline images. Only for images hosted in this gitea instance.
+;EMBED_ATTACHMENT_IMAGES = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2398,6 +2462,8 @@ LEVEL = Info
;DEFAULT_GIT_TREES_PER_PAGE = 1000
;; Default max size of a blob returned by the blobs API (default is 10MiB)
;DEFAULT_MAX_BLOB_SIZE = 10485760
+;; Default max combined size of all blobs returned by the files API (default is 100MiB)
+;DEFAULT_MAX_RESPONSE_SIZE = 104857600
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2438,7 +2504,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits)
-;MERMAID_MAX_SOURCE_CHARACTERS = 5000
+;MERMAID_MAX_SOURCE_CHARACTERS = 50000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2559,9 +2625,6 @@ LEVEL = Info
;; Currently, only `minio` and `azureblob` is supported.
;SERVE_DIRECT = false
;;
-;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload`
-;CHUNKED_UPLOAD_PATH = tmp/package-upload
-;;
;; Maximum count of package versions a single owner can have (`-1` means no limits)
;LIMIT_TOTAL_OWNER_COUNT = -1
;; Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
diff --git a/docker/manifest.rootless.tmpl b/docker/manifest.rootless.tmpl
index 1ebf5b73c8..3fa94ab0ec 100644
--- a/docker/manifest.rootless.tmpl
+++ b/docker/manifest.rootless.tmpl
@@ -22,3 +22,8 @@ manifests:
architecture: arm64
os: linux
variant: v8
+ -
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64-rootless
+ platform:
+ architecture: riscv64
+ os: linux
diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl
index 08ccf61b57..c68ca46dd8 100644
--- a/docker/manifest.tmpl
+++ b/docker/manifest.tmpl
@@ -22,3 +22,8 @@ manifests:
architecture: arm64
os: linux
variant: v8
+ -
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64
+ platform:
+ architecture: riscv64
+ os: linux
diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup
index dbb3bafd35..48e7d4b211 100755
--- a/docker/root/etc/s6/openssh/setup
+++ b/docker/root/etc/s6/openssh/setup
@@ -31,6 +31,21 @@ if [ -e /data/ssh/ssh_host_ecdsa_cert ]; then
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
fi
+# In case someone wants to sign the `{keyname}.pub` key by `ssh-keygen -s ca -I identity ...` to
+# make use of the ssh-key certificate authority feature (see ssh-keygen CERTIFICATES section),
+# the generated key file name is `{keyname}-cert.pub`
+if [ -e /data/ssh/ssh_host_ed25519_key-cert.pub ]; then
+ SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519_key-cert.pub"}
+fi
+
+if [ -e /data/ssh/ssh_host_rsa_key-cert.pub ]; then
+ SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa_key-cert.pub"}
+fi
+
+if [ -e /data/ssh/ssh_host_ecdsa_key-cert.pub ]; then
+ SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_key-cert.pub"}
+fi
+
if [ -d /etc/ssh ]; then
SSH_PORT=${SSH_PORT:-"22"} \
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
diff --git a/docker/root/usr/bin/entrypoint b/docker/root/usr/bin/entrypoint
index d9dbb3ebe0..08587fc4f4 100755
--- a/docker/root/usr/bin/entrypoint
+++ b/docker/root/usr/bin/entrypoint
@@ -37,5 +37,5 @@ done
if [ $# -gt 0 ]; then
exec "$@"
else
- exec /bin/s6-svscan /etc/s6
+ exec /usr/bin/s6-svscan /etc/s6
fi
diff --git a/flake.lock b/flake.lock
index 1890b82dcf..da3f19bbd2 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1726560853,
- "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1731139594,
- "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
+ "lastModified": 1747179050,
+ "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
+ "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index e3655b627e..1b930649d0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -29,9 +29,14 @@
poetry
# backend
+ go_1_24
gofumpt
sqlite
];
+ shellHook = ''
+ export GO="${pkgs.go_1_24}/bin/go"
+ export GOROOT="${pkgs.go_1_24}/share/go"
+ '';
};
}
);
diff --git a/go.mod b/go.mod
index 671151d4b6..afe7c990e4 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module code.gitea.io/gitea
-go 1.23
+go 1.24.4
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
// But some CAs use negative serial number, just relax the check. related:
@@ -8,11 +8,11 @@ go 1.23
godebug x509negativeserial=1
require (
- code.gitea.io/actions-proto-go v0.4.0
+ code.gitea.io/actions-proto-go v0.4.1
code.gitea.io/gitea-vet v0.2.3
- code.gitea.io/sdk/gitea v0.19.0
+ code.gitea.io/sdk/gitea v0.21.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
- connectrpc.com/connect v1.17.0
+ connectrpc.com/connect v1.18.1
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
@@ -21,20 +21,20 @@ require (
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/httpsig v1.2.2
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
- github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
- github.com/ProtonMail/go-crypto v1.0.0
- github.com/PuerkitoBio/goquery v1.10.0
+ github.com/ProtonMail/go-crypto v1.2.0
+ github.com/PuerkitoBio/goquery v1.10.3
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
- github.com/alecthomas/chroma/v2 v2.14.0
- github.com/aws/aws-sdk-go v1.55.5
- github.com/aws/aws-sdk-go-v2/credentials v1.17.42
- github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
+ github.com/alecthomas/chroma/v2 v2.17.0
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.67
+ github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
- github.com/blevesearch/bleve/v2 v2.4.2
- github.com/buildkite/terminal-to-html/v3 v3.16.3
- github.com/caddyserver/certmagic v0.21.4
+ github.com/blevesearch/bleve/v2 v2.5.0
+ github.com/bohde/codel v0.2.0
+ github.com/buildkite/terminal-to-html/v3 v3.16.8
+ github.com/caddyserver/certmagic v0.23.0
github.com/charmbracelet/git-lfs-transfer v0.2.0
github.com/chi-middleware/proxy v1.1.1
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
@@ -42,38 +42,35 @@ require (
github.com/djherbis/nio/v3 v3.0.1
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
github.com/dustin/go-humanize v1.0.1
- github.com/editorconfig/editorconfig-core-go/v2 v2.6.2
+ github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/emersion/go-imap v1.2.1
github.com/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.5
- github.com/fsnotify/fsnotify v1.7.0
+ github.com/fsnotify/fsnotify v1.9.0
github.com/gliderlabs/ssh v0.3.8
- github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c
+ github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
- github.com/go-chi/chi/v5 v5.1.0
+ github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
- github.com/go-enry/go-enry/v2 v2.9.1
- github.com/go-git/go-billy/v5 v5.6.0
- github.com/go-git/go-git/v5 v5.12.0
- github.com/go-ldap/ldap/v3 v3.4.8
+ github.com/go-enry/go-enry/v2 v2.9.2
+ github.com/go-git/go-billy/v5 v5.6.2
+ github.com/go-git/go-git/v5 v5.16.0
+ github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-redsync/redsync/v4 v4.13.0
- github.com/go-sql-driver/mysql v1.8.1
- github.com/go-swagger/go-swagger v0.31.0
- github.com/go-testfixtures/testfixtures/v3 v3.11.0
- github.com/go-webauthn/webauthn v0.11.2
+ github.com/go-sql-driver/mysql v1.9.2
+ github.com/go-webauthn/webauthn v0.12.3
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
- github.com/golang-jwt/jwt/v5 v5.2.1
- github.com/google/go-github/v61 v61.0.0
+ github.com/golang-jwt/jwt/v5 v5.2.2
+ github.com/google/go-github/v71 v71.0.0
github.com/google/licenseclassifier/v2 v2.0.0
- github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db
+ github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
- github.com/h2non/gock v1.2.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
@@ -81,223 +78,179 @@ require (
github.com/jhillyerd/enmime v1.3.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
- github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
- github.com/klauspost/compress v1.17.11
- github.com/klauspost/cpuid/v2 v2.2.8
+ github.com/klauspost/compress v1.18.0
+ github.com/klauspost/cpuid/v2 v2.2.10
github.com/lib/pq v1.10.9
- github.com/markbates/goth v1.80.0
+ github.com/markbates/goth v1.81.0
github.com/mattn/go-isatty v0.0.20
- github.com/mattn/go-sqlite3 v1.14.24
- github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a
+ github.com/mattn/go-sqlite3 v1.14.28
+ github.com/meilisearch/meilisearch-go v0.31.0
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27
- github.com/microsoft/go-mssqldb v1.7.2
- github.com/minio/minio-go/v7 v7.0.80
+ github.com/microsoft/go-mssqldb v1.8.0
+ github.com/minio/minio-go/v7 v7.0.91
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.63
- github.com/niklasfasching/go-org v1.7.0
+ github.com/niklasfasching/go-org v1.8.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
- github.com/opencontainers/image-spec v1.1.0
+ github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
- github.com/prometheus/client_golang v1.20.5
+ github.com/prometheus/client_golang v1.22.0
github.com/quasoft/websspi v1.1.2
- github.com/redis/go-redis/v9 v9.7.0
+ github.com/redis/go-redis/v9 v9.7.3
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
- github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.12
- github.com/urfave/cli/v2 v2.27.5
- github.com/wneessen/go-mail v0.5.2
- github.com/xanzy/go-gitlab v0.112.0
+ github.com/urfave/cli-docs/v3 v3.0.0-alpha6
+ github.com/urfave/cli/v3 v3.3.3
+ github.com/wneessen/go-mail v0.6.2
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
- github.com/yuin/goldmark v1.7.8
+ github.com/yuin/goldmark v1.7.10
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
- golang.org/x/crypto v0.31.0
- golang.org/x/image v0.21.0
- golang.org/x/net v0.30.0
- golang.org/x/oauth2 v0.23.0
- golang.org/x/sync v0.10.0
- golang.org/x/sys v0.28.0
- golang.org/x/text v0.21.0
- golang.org/x/tools v0.26.0
- google.golang.org/grpc v1.67.1
- google.golang.org/protobuf v1.35.1
+ gitlab.com/gitlab-org/api/client-go v0.127.0
+ golang.org/x/crypto v0.39.0
+ golang.org/x/image v0.26.0
+ golang.org/x/net v0.40.0
+ golang.org/x/oauth2 v0.29.0
+ golang.org/x/sync v0.15.0
+ golang.org/x/sys v0.33.0
+ golang.org/x/text v0.26.0
+ google.golang.org/grpc v1.72.0
+ google.golang.org/protobuf v1.36.6
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
- mvdan.cc/xurls/v2 v2.5.0
+ mvdan.cc/xurls/v2 v2.6.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
)
require (
- cloud.google.com/go/compute/metadata v0.5.2 // indirect
+ cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
- github.com/ClickHouse/ch-go v0.63.1 // indirect
- github.com/ClickHouse/clickhouse-go/v2 v2.24.0 // indirect
- github.com/DataDog/zstd v1.5.6 // indirect
- github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.3.0 // indirect
- github.com/Masterminds/sprig/v3 v3.3.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
+ github.com/DataDog/zstd v1.5.7 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/RoaringBitmap/roaring v1.9.4 // indirect
+ github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
- github.com/andybalholm/cascadia v1.3.2 // indirect
+ github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
- github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
- github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
- github.com/aws/smithy-go v1.22.0 // indirect
+ github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
+ github.com/aws/smithy-go v1.22.3 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/bits-and-blooms/bitset v1.14.3 // indirect
- github.com/blevesearch/bleve_index_api v1.1.12 // indirect
- github.com/blevesearch/geo v0.1.20 // indirect
- github.com/blevesearch/go-faiss v1.0.23 // indirect
+ github.com/bits-and-blooms/bitset v1.22.0 // indirect
+ github.com/blevesearch/bleve_index_api v1.2.8 // indirect
+ github.com/blevesearch/geo v0.2.0 // indirect
+ github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
- github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
+ github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
- github.com/blevesearch/vellum v1.0.10 // indirect
- github.com/blevesearch/zapx/v11 v11.3.10 // indirect
- github.com/blevesearch/zapx/v12 v12.3.10 // indirect
- github.com/blevesearch/zapx/v13 v13.3.10 // indirect
- github.com/blevesearch/zapx/v14 v14.3.10 // indirect
- github.com/blevesearch/zapx/v15 v15.3.16 // indirect
- github.com/blevesearch/zapx/v16 v16.1.7 // indirect
+ github.com/blevesearch/vellum v1.1.0 // indirect
+ github.com/blevesearch/zapx/v11 v11.4.1 // indirect
+ github.com/blevesearch/zapx/v12 v12.4.1 // indirect
+ github.com/blevesearch/zapx/v13 v13.4.1 // indirect
+ github.com/blevesearch/zapx/v14 v14.4.1 // indirect
+ github.com/blevesearch/zapx/v15 v15.4.1 // indirect
+ github.com/blevesearch/zapx/v16 v16.2.3 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
- github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
+ github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/cloudflare/circl v1.5.0 // indirect
+ github.com/cloudflare/circl v1.6.1 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
- github.com/couchbase/gomemcached v0.3.2 // indirect
+ github.com/couchbase/gomemcached v0.3.3 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
- github.com/cyphar/filepath-securejoin v0.3.4 // indirect
+ github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dlclark/regexp2 v1.11.4 // indirect
+ github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
- github.com/felixge/httpsnoop v1.0.4 // indirect
- github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
- github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect
- github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
+ github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect
+ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
- github.com/go-faster/city v1.0.1 // indirect
- github.com/go-faster/errors v0.7.1 // indirect
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
- github.com/go-openapi/analysis v0.23.0 // indirect
- github.com/go-openapi/errors v0.22.0 // indirect
- github.com/go-openapi/inflect v0.21.0 // indirect
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
- github.com/go-openapi/jsonreference v0.21.0 // indirect
- github.com/go-openapi/loads v0.22.0 // indirect
- github.com/go-openapi/runtime v0.28.0 // indirect
- github.com/go-openapi/spec v0.21.0 // indirect
- github.com/go-openapi/strfmt v0.23.0 // indirect
- github.com/go-openapi/swag v0.23.0 // indirect
- github.com/go-openapi/validate v0.24.0 // indirect
- github.com/go-webauthn/x v0.1.15 // indirect
- github.com/goccy/go-json v0.10.3 // indirect
- github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+ github.com/go-webauthn/x v0.1.20 // indirect
+ github.com/goccy/go-json v0.10.5 // indirect
+ github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
- github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/golang/snappy v0.0.4 // indirect
+ github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
+ github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
- github.com/google/go-tpm v0.9.1 // indirect
+ github.com/google/go-tpm v0.9.3 // indirect
github.com/gorilla/css v1.0.1 // indirect
- github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
- github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
- github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/jessevdk/go-flags v1.6.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
- github.com/kr/pretty v0.3.1 // indirect
- github.com/kr/text v0.2.0 // indirect
- github.com/libdns/libdns v0.2.2 // indirect
- github.com/magiconair/properties v1.8.7 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/libdns/libdns v1.0.0-beta.1 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/mholt/acmez/v2 v2.0.3 // indirect
- github.com/miekg/dns v1.1.62 // indirect
+ github.com/mattn/go-shellwords v1.0.12 // indirect
+ github.com/mholt/acmez/v3 v3.1.2 // indirect
+ github.com/miekg/dns v1.1.65 // indirect
+ github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
- github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
- github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
- github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
- github.com/paulmach/orb v0.11.1 // indirect
- github.com/pelletier/go-toml/v2 v2.2.3 // indirect
- github.com/pierrec/lz4/v4 v4.1.21 // indirect
- github.com/pjbgf/sha1cd v0.3.0 // indirect
+ github.com/pierrec/lz4/v4 v4.1.22 // indirect
+ github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.1 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/rhysd/actionlint v1.7.3 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.63.0 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/rhysd/actionlint v1.7.7 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
- github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/sagikazarmark/locafero v0.6.0 // indirect
- github.com/sagikazarmark/slog-shim v0.1.0 // indirect
- github.com/segmentio/asm v1.2.0 // indirect
- github.com/shopspring/decimal v1.4.0 // indirect
- github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/skeema/knownhosts v1.3.0 // indirect
- github.com/sourcegraph/conc v0.3.0 // indirect
- github.com/spf13/afero v1.11.0 // indirect
- github.com/spf13/cast v1.7.0 // indirect
- github.com/spf13/pflag v1.0.5 // indirect
- github.com/spf13/viper v1.19.0 // indirect
+ github.com/skeema/knownhosts v1.3.1 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
- github.com/subosito/gotenv v1.6.0 // indirect
- github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
@@ -305,29 +258,25 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
- github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
- go.etcd.io/bbolt v1.3.11 // indirect
- go.mongodb.org/mongo-driver v1.17.1 // indirect
- go.opentelemetry.io/otel v1.31.0 // indirect
- go.opentelemetry.io/otel/trace v1.31.0 // indirect
+ go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/time v0.7.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
+ go.uber.org/zap/exp v0.3.0 // indirect
+ golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
+ golang.org/x/mod v0.25.0 // indirect
+ golang.org/x/time v0.11.0 // indirect
+ golang.org/x/tools v0.33.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
-replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
-
-replace github.com/nektos/act => gitea.com/gitea/act v0.261.3
+replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
@@ -335,6 +284,8 @@ replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-tra
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
+replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
+
exclude github.com/gofrs/uuid v3.2.0+incompatible
exclude github.com/gofrs/uuid v4.0.0+incompatible
diff --git a/go.sum b/go.sum
index afa3abece8..2e7c51f747 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,25 @@
-cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
-cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
-code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
-code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
+code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLrKfls=
+code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
-code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y=
-code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
+code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
+code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
-connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk=
-connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
+connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
+connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
-gitea.com/gitea/act v0.261.3 h1:BhiYpGJQKGq0XMYYICCYAN4KnsEWHyLbA6dxhZwFcV4=
-gitea.com/gitea/act v0.261.3/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
+gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY=
+gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
+gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
+gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
@@ -40,56 +40,46 @@ github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815 h1:5EoemV++kUK2Sw98yW
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815/go.mod h1:zjsWZdDLrcDojDIfpQg7A6J4YZLT0cbwuAD26AppDBo=
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/ClickHouse/ch-go v0.63.1 h1:s2JyZvWLTCSAGdtjMBBmAgQQHMco6pawLJMOXi0FODM=
-github.com/ClickHouse/ch-go v0.63.1/go.mod h1:I1kJJCL3WJcBMGe1m+HVK0+nREaG+JOYYBWjrDrF3R0=
-github.com/ClickHouse/clickhouse-go/v2 v2.24.0 h1:L/n/pVVpk95KtkHOiKuSnO7cu2ckeW4gICbbOh5qs74=
-github.com/ClickHouse/clickhouse-go/v2 v2.24.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ=
-github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
-github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
+github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
-github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
-github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
-github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
-github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
-github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
-github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
-github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
+github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
+github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
+github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
+github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
-github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
-github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
+github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
+github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
-github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
-github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
-github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
+github.com/alecthomas/chroma/v2 v2.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
+github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -100,29 +90,25 @@ github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnons
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
-github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
-github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
+github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
+github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
-github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
-github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
-github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ=
-github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3 h1:NbAYtQnTXzv4u5uesdDhXGWDTsEtTyFXlzfXELtI6cc=
-github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3/go.mod h1:ZhgiuB7I3d3UU6PnCWwb0IYFgGzJz0VT+DK3Xv1xtF8=
-github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
-github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
+github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
+github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
+github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2 h1:enL75gIdaPAoBztv/GDuMgOocEUpO2jYc45qp2Uweqs=
+github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2/go.mod h1:JsdLne5QNlqJdCQFm2DbHLNmNfEWSU7HnTuvi8SIl+E=
+github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
+github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -130,20 +116,20 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
-github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
+github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blevesearch/bleve/v2 v2.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM=
-github.com/blevesearch/bleve/v2 v2.4.2 h1:NooYP1mb3c0StkiY9/xviiq2LGSaE8BQBCc/pirMx0U=
-github.com/blevesearch/bleve/v2 v2.4.2/go.mod h1:ATNKj7Yl2oJv/lGuF4kx39bST2dveX6w0th2FFYLkc8=
+github.com/blevesearch/bleve/v2 v2.5.0 h1:HzYqBy/5/M9Ul9ESEmXzN/3Jl7YpmWBdHM/+zzv/3k4=
+github.com/blevesearch/bleve/v2 v2.5.0/go.mod h1:PcJzTPnEynO15dCf9isxOga7YFRa/cMSsbnRwnszXUk=
github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
-github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
-github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
-github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
-github.com/blevesearch/go-faiss v1.0.23 h1:Wmc5AFwDLKGl2L6mjLX1Da3vCL0EKa2uHHSorcIS1Uc=
-github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
+github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
+github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
+github.com/blevesearch/geo v0.2.0 h1:f+IE3/C3mGeXDyhtMbWel6BgqBqaOUz43GtWg26GlB0=
+github.com/blevesearch/geo v0.2.0/go.mod h1:k8Hyfz12kM8QmeWLhgX7VMMCoVFmttBnr62V5zniXak=
+github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
+github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@@ -152,8 +138,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.0.1/go.mod h1:lq7yK2jQy1yQjtjTfU931aVqz7pYxEudHaDwOt1tXfU=
-github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
-github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
+github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s=
+github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@@ -164,39 +150,43 @@ github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMG
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.0.3/go.mod h1:2u5ax02KeDuNWu4/C+hVQMD6uLN4txH1JbtpaDNLJRo=
github.com/blevesearch/vellum v1.0.4/go.mod h1:cMhywHI0de50f7Nj42YgvyD6bFJ2WkNRvNBlNMrEVgY=
-github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
-github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
+github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
+github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM=
-github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
-github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
+github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA=
+github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs=
github.com/blevesearch/zapx/v12 v12.2.0/go.mod h1:fdjwvCwWWwJW/EYTYGtAp3gBA0geCYGLcVTtJEZnY6A=
-github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
-github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
+github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ=
+github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II=
github.com/blevesearch/zapx/v13 v13.2.0/go.mod h1:o5rAy/lRS5JpAbITdrOHBS/TugWYbkcYZTz6VfEinAQ=
-github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
-github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
+github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE=
+github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8=
github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCquiEKYh1xLFK9g=
-github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
-github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
+github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE=
+github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms=
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
-github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
-github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
-github.com/blevesearch/zapx/v16 v16.1.7 h1:I07qV6l1rPda19zyof9Q2J9E8cjZ57pQhNY0+ePI5vM=
-github.com/blevesearch/zapx/v16 v16.1.7/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8=
+github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g=
+github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y=
+github.com/blevesearch/zapx/v16 v16.2.3 h1:7Y0r+a3diEvlazsncexq1qoFOcBd64xwMS7aDm4lo1s=
+github.com/blevesearch/zapx/v16 v16.2.3/go.mod h1:wVJ+GtURAaRG9KQAMNYyklq0egV+XJlGcXNCE0OFjjA=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
+github.com/bohde/codel v0.2.0 h1:fzF7ibgKmCfQbOzQCblmQcwzDRmV7WO7VMLm/hDvD3E=
+github.com/bohde/codel v0.2.0/go.mod h1:Idb1IRvTdwkRjIjguLIo+FXhIBhcpGl94o7xra6ggWk=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
+github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
+github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
-github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk=
-github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
-github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
-github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
-github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
+github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc=
+github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM=
+github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
+github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
@@ -211,17 +201,16 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
-github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
-github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
-github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
+github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/go-couchbase v0.1.1 h1:ClFXELcKj/ojyoTYbsY34QUrrYCBi/1G749sXSCkdhk=
github.com/couchbase/go-couchbase v0.1.1/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
-github.com/couchbase/gomemcached v0.3.2 h1:08rxiOoNcv0x5LTxgcYhnx1aPvV7iEtfeyUgqsJyPk0=
-github.com/couchbase/gomemcached v0.3.2/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
+github.com/couchbase/gomemcached v0.3.3 h1:D7qqXLO8wNa4pn5oE65lT3pA3IeStn4joT7/JgGXzKc=
+github.com/couchbase/gomemcached v0.3.3/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP9g3Wfhl1JJw68=
github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9BCs=
github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
@@ -229,16 +218,14 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
-github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
+github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
+github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
-github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
-github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA=
@@ -251,19 +238,19 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
-github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
-github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w=
-github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY=
+github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
+github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
+github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
@@ -279,84 +266,53 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
-github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
-github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
+github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
-github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c h1:82lzmsy5Nr6JA6HcLRVxGfbdSoWfW45C6jnY3zFS7Ks=
-github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c/go.mod h1:rpIPGre4qtTgSpVT0zz3hycAMuLtUt7BNngVNpyXhL8=
-github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 h1:eQEXAzWEijFbwtm/Hr2EtFbM0LvATRd1ltpDb+mfjQk=
-github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo=
+github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d h1:IWrWGnmKzpHqginJ18ljKkty/X8glxM8Mg3pk6bkb8g=
+github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d/go.mod h1:EUtZuXtHo4yKkTJmcbAZYW+X1G2poeT8icmBh24eq7o=
+github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 h1:tlwla5IQUea0CuktkBd2FLDwVzts4OeTWPPkhQPSK5Q=
+github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
-github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
-github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
-github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
-github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
+github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
-github.com/go-enry/go-enry/v2 v2.9.1 h1:G9iDteJ/Mc0F4Di5NeQknf83R2OkRbwY9cAYmcqVG6U=
-github.com/go-enry/go-enry/v2 v2.9.1/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
+github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
+github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
-github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
-github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
-github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
-github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
-github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
+github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
+github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
-github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
+github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
-github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
-github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
-github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
-github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
-github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
-github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
-github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=
-github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
-github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
-github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
-github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
-github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
-github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
-github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
-github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
-github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
-github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
-github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
-github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
-github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
+github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
+github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
@@ -365,43 +321,37 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA=
github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ=
-github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
-github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
-github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc=
-github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po=
+github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
+github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
-github.com/go-testfixtures/testfixtures/v3 v3.11.0 h1:XxQr8AnPORcZkyNd7go5UNLPD3dULN8ixYISlzrlfEQ=
-github.com/go-testfixtures/testfixtures/v3 v3.11.0/go.mod h1:THmudHF1Ixq++J2/UodcJpxUphfyEd77m83TvDtryqE=
-github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=
-github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=
-github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU=
-github.com/go-webauthn/x v0.1.15/go.mod h1:pf7VI23raFLHPO9VVIs9/u1etqwAOP0S2KoHGL6WbZ8=
+github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
+github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
+github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
+github.com/go-webauthn/x v0.1.20/go.mod h1:n/gAc8ssZJGATM0qThE+W+vfgXiMedsWi3wf/C4lld0=
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=
-github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
-github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
-github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
-github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I=
-github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -410,39 +360,42 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
+github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go=
-github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-github/v71 v71.0.0 h1:Zi16OymGKZZMm8ZliffVVJ/Q9YZreDKONCr+WUd0Z30=
+github.com/google/go-github/v71 v71.0.0/go.mod h1:URZXObp2BLlMjwu0O8g4y6VBneUj2bCHgnI8FfgZ51M=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
-github.com/google/go-tpm v0.9.1 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=
-github.com/google/go-tpm v0.9.1/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
+github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
+github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA=
github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416 h1:1/qwHx8P72glDXdyCKesJ+/c40x71SY4q2avOxJ2iYQ=
+github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -455,8 +408,6 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
-github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
-github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
@@ -465,13 +416,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
-github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
-github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
-github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
-github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -483,12 +429,10 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
@@ -497,22 +441,6 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
-github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
-github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
-github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
-github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
-github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
-github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
-github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
-github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
-github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
-github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
-github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@@ -529,14 +457,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
-github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
-github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
-github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
-github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -548,19 +470,14 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
-github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
-github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
-github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -579,57 +496,52 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
-github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
-github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI=
-github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk=
+github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
+github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
-github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE=
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
-github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
-github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/markbates/goth v1.81.0 h1:XVcCkeGWokynPV7MXvgb8pd2s3r7DS40P7931w6kdnE=
+github.com/markbates/goth v1.81.0/go.mod h1:+6z31QyUms84EHmuBY7iuqYSxyoN3njIgg9iCF/lR1k=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
-github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a h1:F0y+3QtCG00mr4KueQWuHv1tlIQeNXhH+XAKYLhb3X4=
-github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a/go.mod h1:NYOgjEGt/+oExD+NixreBMqxtIB0kCndXOOgpGhoqEs=
-github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
-github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
+github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
+github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY=
+github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g=
+github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
+github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
-github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
-github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
-github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
-github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
+github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
+github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
+github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
+github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
+github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
+github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk=
-github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
-github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc=
+github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
-github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
@@ -639,18 +551,14 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
-github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
-github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
-github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
+github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
+github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
@@ -667,21 +575,16 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
-github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
-github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
-github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
-github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
-github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
-github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
+github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
+github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -692,25 +595,25 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
-github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
+github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
-github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
+github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rhysd/actionlint v1.7.3 h1:WD919WuLYrSCwY8VGBqJBEuzyVEIL5viXmXqRRcKOVs=
-github.com/rhysd/actionlint v1.7.3/go.mod h1:rl+8ZoX1rqnbcMWKaTyOHmw08mmb/zlmG/Zu1fY47F4=
+github.com/rhysd/actionlint v1.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k=
+github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -719,59 +622,38 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
-github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
-github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
-github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
-github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
-github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
-github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
-github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
-github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
-github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
-github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
-github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
+github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
+github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
-github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
-github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
-github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
-github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
@@ -779,28 +661,24 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
-github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
-github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
-github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
-github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -810,22 +688,19 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
-github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
-github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/urfave/cli-docs/v3 v3.0.0-alpha6 h1:w/l/N0xw1rO/aHRIGXJ0lDwwYFOzilup1qGvIytP3BI=
+github.com/urfave/cli-docs/v3 v3.0.0-alpha6/go.mod h1:p7Z4lg8FSTrPB9GTaNyTrK3ygffHZcK3w0cU2VE+mzU=
+github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=
+github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8=
-github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
+github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
+github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
-github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
-github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -836,20 +711,16 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
-github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
+github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
@@ -860,16 +731,11 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
+gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U=
+gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
-go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
-go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
-go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
-go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
-go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
-go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
-go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
-go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
+go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
+go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -879,6 +745,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
+go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -886,20 +754,18 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
-golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
-golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
+golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
+golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -909,12 +775,11 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -922,34 +787,32 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
-golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
+golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
+golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -973,83 +836,73 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
-golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
-golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
-google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
-google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
+google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
-google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1071,6 +924,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
@@ -1093,8 +947,10 @@ modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
-mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
+mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
+mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
+pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0=
+pgregory.net/rapid v0.4.2/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
diff --git a/main.go b/main.go
index 756c3e0f9b..2c25bac4e3 100644
--- a/main.go
+++ b/main.go
@@ -21,7 +21,7 @@ import (
_ "code.gitea.io/gitea/modules/markup/markdown"
_ "code.gitea.io/gitea/modules/markup/orgmode"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// these flags will be set by the build flags
diff --git a/main_timezones.go b/main_timezones.go
new file mode 100644
index 0000000000..e1233007c6
--- /dev/null
+++ b/main_timezones.go
@@ -0,0 +1,16 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//go:build windows
+
+package main
+
+// Golang has the ability to load OS's timezone data from most UNIX systems (https://github.com/golang/go/blob/master/src/time/zoneinfo_unix.go)
+// Even if the timezone data is missing, users could install the related packages to get it.
+// But on Windows, although `zoneinfo_windows.go` tries to load the timezone data from Windows registry,
+// some users still suffer from the issue that the timezone data is missing: https://github.com/go-gitea/gitea/issues/33235
+// So we import the tzdata package to make sure the timezone data is included in the binary.
+//
+// For non-Windows package builders, they could still use the "TAGS=timetzdata" to include the tzdata package in the binary.
+// If we decided to add the tzdata for other platforms, modify the "go:build" directive above.
+import _ "time/tzdata"
diff --git a/models/actions/artifact.go b/models/actions/artifact.go
index 0bc66ba24e..757bd13acd 100644
--- a/models/actions/artifact.go
+++ b/models/actions/artifact.go
@@ -30,6 +30,25 @@ const (
ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted
)
+func (status ArtifactStatus) ToString() string {
+ switch status {
+ case ArtifactStatusUploadPending:
+ return "upload is not yet completed"
+ case ArtifactStatusUploadConfirmed:
+ return "upload is completed"
+ case ArtifactStatusUploadError:
+ return "upload failed"
+ case ArtifactStatusExpired:
+ return "expired"
+ case ArtifactStatusPendingDeletion:
+ return "pending deletion"
+ case ArtifactStatusDeleted:
+ return "deleted"
+ default:
+ return "unknown"
+ }
+}
+
func init() {
db.RegisterModel(new(ActionArtifact))
}
@@ -48,7 +67,7 @@ type ActionArtifact struct {
ContentEncoding string // The content encoding of the artifact
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
- Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
+ Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
@@ -68,7 +87,7 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
RepoID: t.RepoID,
OwnerID: t.OwnerID,
CommitSHA: t.CommitSHA,
- Status: int64(ArtifactStatusUploadPending),
+ Status: ArtifactStatusUploadPending,
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
}
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
@@ -108,12 +127,19 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro
type FindArtifactsOptions struct {
db.ListOptions
- RepoID int64
- RunID int64
- ArtifactName string
- Status int
+ RepoID int64
+ RunID int64
+ ArtifactName string
+ Status int
+ FinalizedArtifactsV4 bool
}
+func (opts FindArtifactsOptions) ToOrders() string {
+ return "id"
+}
+
+var _ db.FindOptionsOrder = (*FindArtifactsOptions)(nil)
+
func (opts FindArtifactsOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
@@ -128,11 +154,15 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond {
if opts.Status > 0 {
cond = cond.And(builder.Eq{"status": opts.Status})
}
+ if opts.FinalizedArtifactsV4 {
+ cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
+ cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
+ }
return cond
}
-// ActionArtifactMeta is the meta data of an artifact
+// ActionArtifactMeta is the meta-data of an artifact
type ActionArtifactMeta struct {
ArtifactName string
FileSize int64
@@ -166,18 +196,18 @@ func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifa
// SetArtifactExpired sets an artifact to expired
func SetArtifactExpired(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)})
+ _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
return err
}
// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it
func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error {
- _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)})
+ _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
return err
}
// SetArtifactDeleted sets an artifact to deleted
func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
+ _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
return err
}
diff --git a/models/actions/run.go b/models/actions/run.go
index f40bc1eb3d..f0ab61b200 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -5,6 +5,7 @@ package actions
import (
"context"
+ "errors"
"fmt"
"slices"
"strings"
@@ -15,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -88,7 +90,7 @@ func (run *ActionRun) RefLink() string {
if refName.IsPull() {
return run.Repo.Link() + "/pulls/" + refName.ShortName()
}
- return git.RefURL(run.Repo.Link(), run.Ref)
+ return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
}
// PrettyRef return #id for pull ref or ShortName for others
@@ -154,7 +156,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
}
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
- if run.Event == webhook_module.HookEventPullRequest || run.Event == webhook_module.HookEventPullRequestSync {
+ if run.Event.IsPullRequest() {
var payload api.PullRequestPayload
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
return nil, err
@@ -164,12 +166,24 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err
return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
}
+func (run *ActionRun) GetWorkflowRunEventPayload() (*api.WorkflowRunPayload, error) {
+ if run.Event == webhook_module.HookEventWorkflowRun {
+ var payload api.WorkflowRunPayload
+ if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
+ return nil, err
+ }
+ return &payload, nil
+ }
+ return nil, fmt.Errorf("event %s is not a workflow run event", run.Event)
+}
+
func (run *ActionRun) IsSchedule() bool {
return run.ScheduleID > 0
}
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID).
+ NoAutoTime().
SetExpr("num_action_runs",
builder.Select("count(*)").From("action_run").
Where(builder.Eq{"repo_id": repo.ID}),
@@ -194,7 +208,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
-func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
+func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) ([]*ActionRunJob, error) {
// Find all runs in the specified repository, reference, and workflow with non-final status
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
RepoID: repoID,
@@ -204,14 +218,16 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
Status: []Status{StatusRunning, StatusWaiting, StatusBlocked},
})
if err != nil {
- return err
+ return nil, err
}
// If there are no runs found, there's no need to proceed with cancellation, so return nil.
if total == 0 {
- return nil
+ return nil, nil
}
+ cancelledJobs := make([]*ActionRunJob, 0, total)
+
// Iterate over each found run and cancel its associated jobs.
for _, run := range runs {
// Find all jobs associated with the current run.
@@ -219,7 +235,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
RunID: run.ID,
})
if err != nil {
- return err
+ return cancelledJobs, err
}
// Iterate over each job and attempt to cancel it.
@@ -238,27 +254,29 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
// Update the job's status and stopped time in the database.
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
if err != nil {
- return err
+ return cancelledJobs, err
}
// If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
if n == 0 {
- return fmt.Errorf("job has changed, try again")
+ return cancelledJobs, errors.New("job has changed, try again")
}
+ cancelledJobs = append(cancelledJobs, job)
// Continue with the next job.
continue
}
// If the job has an associated task, try to stop the task, effectively cancelling the job.
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
- return err
+ return cancelledJobs, err
}
+ cancelledJobs = append(cancelledJobs, job)
}
}
// Return nil to indicate successful cancellation of all running and waiting jobs.
- return nil
+ return cancelledJobs, nil
}
// InsertRun inserts a run
@@ -275,7 +293,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return err
}
run.Index = index
- run.Title, _ = util.SplitStringAtByteN(run.Title, 255)
+ run.Title = util.EllipsisDisplayString(run.Title, 255)
if err := db.Insert(ctx, run); err != nil {
return err
@@ -308,7 +326,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
} else {
hasWaiting = true
}
- job.Name, _ = util.SplitStringAtByteN(job.Name, 255)
+ job.Name = util.EllipsisDisplayString(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID,
RepoID: run.RepoID,
@@ -337,13 +355,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return committer.Commit()
}
-func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
+func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
var run ActionRun
- has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
+ has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", runID, repoID).Get(&run)
if err != nil {
return nil, err
} else if !has {
- return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist)
+ return nil, fmt.Errorf("run with id %d: %w", runID, util.ErrNotExist)
}
return &run, nil
@@ -402,29 +420,22 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
if len(cols) > 0 {
sess.Cols(cols...)
}
- run.Title, _ = util.SplitStringAtByteN(run.Title, 255)
+ run.Title = util.EllipsisDisplayString(run.Title, 255)
affected, err := sess.Update(run)
if err != nil {
return err
}
if affected == 0 {
- return fmt.Errorf("run has changed")
+ return errors.New("run has changed")
// It's impossible that the run is not found, since Gitea never deletes runs.
}
if run.Status != 0 || slices.Contains(cols, "status") {
if run.RepoID == 0 {
- run, err = GetRunByID(ctx, run.ID)
- if err != nil {
- return err
- }
+ setting.PanicInDevOrTesting("RepoID should not be 0")
}
- if run.Repo == nil {
- repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
- if err != nil {
- return err
- }
- run.Repo = repo
+ if err = run.LoadRepo(ctx); err != nil {
+ return err
}
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
return err
diff --git a/models/actions/run_job.go b/models/actions/run_job.go
index de4b6aab66..bad895036d 100644
--- a/models/actions/run_job.go
+++ b/models/actions/run_job.go
@@ -10,6 +10,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -19,11 +20,12 @@ import (
// ActionRunJob represents a job of a run
type ActionRunJob struct {
ID int64
- RunID int64 `xorm:"index"`
- Run *ActionRun `xorm:"-"`
- RepoID int64 `xorm:"index"`
- OwnerID int64 `xorm:"index"`
- CommitSHA string `xorm:"index"`
+ RunID int64 `xorm:"index"`
+ Run *ActionRun `xorm:"-"`
+ RepoID int64 `xorm:"index"`
+ Repo *repo_model.Repository `xorm:"-"`
+ OwnerID int64 `xorm:"index"`
+ CommitSHA string `xorm:"index"`
IsForkPullRequest bool
Name string `xorm:"VARCHAR(255)"`
Attempt int64
@@ -49,7 +51,7 @@ func (job *ActionRunJob) Duration() time.Duration {
func (job *ActionRunJob) LoadRun(ctx context.Context) error {
if job.Run == nil {
- run, err := GetRunByID(ctx, job.RunID)
+ run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return err
}
@@ -58,6 +60,17 @@ func (job *ActionRunJob) LoadRun(ctx context.Context) error {
return nil
}
+func (job *ActionRunJob) LoadRepo(ctx context.Context) error {
+ if job.Repo == nil {
+ repo, err := repo_model.GetRepositoryByID(ctx, job.RepoID)
+ if err != nil {
+ return err
+ }
+ job.Repo = repo
+ }
+ return nil
+}
+
// LoadAttributes load Run if not loaded
func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
if job == nil {
@@ -83,7 +96,7 @@ func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
return &job, nil
}
-func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error) {
+func GetRunJobsByRunID(ctx context.Context, runID int64) (ActionJobList, error) {
var jobs []*ActionRunJob
if err := db.GetEngine(ctx).Where("run_id=?", runID).OrderBy("id").Find(&jobs); err != nil {
return nil, err
@@ -129,7 +142,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
{
// Other goroutines may aggregate the status of the run and update it too.
// So we need load the run and its jobs before updating the run.
- run, err := GetRunByID(ctx, job.RunID)
+ run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return 0, err
}
@@ -172,10 +185,10 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
return StatusSuccess
case hasCancelled:
return StatusCancelled
- case hasFailure:
- return StatusFailure
case hasRunning:
return StatusRunning
+ case hasFailure:
+ return StatusFailure
case hasWaiting:
return StatusWaiting
case hasBlocked:
diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go
index 6c5d3b3252..5f7bb62878 100644
--- a/models/actions/run_job_list.go
+++ b/models/actions/run_job_list.go
@@ -7,6 +7,7 @@ import (
"context"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/timeutil"
@@ -21,7 +22,33 @@ func (jobs ActionJobList) GetRunIDs() []int64 {
})
}
+func (jobs ActionJobList) LoadRepos(ctx context.Context) error {
+ repoIDs := container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
+ return j.RepoID, j.RepoID != 0 && j.Repo == nil
+ })
+ if len(repoIDs) == 0 {
+ return nil
+ }
+
+ repos := make(map[int64]*repo_model.Repository, len(repoIDs))
+ if err := db.GetEngine(ctx).In("id", repoIDs).Find(&repos); err != nil {
+ return err
+ }
+ for _, j := range jobs {
+ if j.RepoID > 0 && j.Repo == nil {
+ j.Repo = repos[j.RepoID]
+ }
+ }
+ return nil
+}
+
func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
+ if withRepo {
+ if err := jobs.LoadRepos(ctx); err != nil {
+ return err
+ }
+ }
+
runIDs := jobs.GetRunIDs()
runs := make(map[int64]*ActionRun, len(runIDs))
if err := db.GetEngine(ctx).In("id", runIDs).Find(&runs); err != nil {
@@ -30,15 +57,9 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
for _, j := range jobs {
if j.RunID > 0 && j.Run == nil {
j.Run = runs[j.RunID]
+ j.Run.Repo = j.Repo
}
}
- if withRepo {
- var runsList RunList = make([]*ActionRun, 0, len(runs))
- for _, r := range runs {
- runsList = append(runsList, r)
- }
- return runsList.LoadRepos(ctx)
- }
return nil
}
@@ -59,22 +80,31 @@ type FindRunJobOptions struct {
func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RunID > 0 {
- cond = cond.And(builder.Eq{"run_id": opts.RunID})
+ cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
}
if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
- }
- if opts.OwnerID > 0 {
- cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
+ cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
}
if opts.CommitSHA != "" {
- cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
+ cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
}
if len(opts.Statuses) > 0 {
- cond = cond.And(builder.In("status", opts.Statuses))
+ cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
}
if opts.UpdatedBefore > 0 {
- cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
+ cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
}
return cond
}
+
+func (opts FindRunJobOptions) ToJoins() []db.JoinFunc {
+ if opts.OwnerID > 0 {
+ return []db.JoinFunc{
+ func(sess db.Engine) error {
+ sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
+ return nil
+ },
+ }
+ }
+ return nil
+}
diff --git a/models/actions/run_job_status_test.go b/models/actions/run_job_status_test.go
index 04fd9ceba7..2a5eb00a6f 100644
--- a/models/actions/run_job_status_test.go
+++ b/models/actions/run_job_status_test.go
@@ -58,18 +58,18 @@ func TestAggregateJobStatus(t *testing.T) {
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
- // failure with other status, fail fast
- // Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
+ // failure with other status, usually fail fast, but "running" wins to match GitHub's behavior
+ // another reason that we can't make "failure" wins over "running": it would cause a weird behavior that user cannot cancel a workflow or get current running workflows correctly by filter after a job fail.
{[]Status{StatusFailure}, StatusFailure},
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
- {[]Status{StatusFailure, StatusRunning}, StatusFailure},
+ {[]Status{StatusFailure, StatusRunning}, StatusRunning},
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
// skipped with other status
- // TODO: need to clarify whether a PR with "skipped" job status is considered as "mergeable" or not.
+ // "all skipped" is also considered as "mergeable" by "services/actions.toCommitStatus", the same as GitHub
{[]Status{StatusSkipped}, StatusSkipped},
{[]Status{StatusSkipped, StatusSuccess}, StatusSuccess},
{[]Status{StatusSkipped, StatusFailure}, StatusFailure},
diff --git a/models/actions/run_list.go b/models/actions/run_list.go
index 4046c7d369..12c55e538e 100644
--- a/models/actions/run_list.go
+++ b/models/actions/run_list.go
@@ -10,6 +10,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/translation"
webhook_module "code.gitea.io/gitea/modules/webhook"
"xorm.io/builder"
@@ -71,39 +72,50 @@ type FindRunOptions struct {
TriggerEvent webhook_module.HookEventType
Approved bool // not util.OptionalBool, it works only when it's true
Status []Status
+ CommitSHA string
}
func (opts FindRunOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
- }
- if opts.OwnerID > 0 {
- cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
+ cond = cond.And(builder.Eq{"`action_run`.repo_id": opts.RepoID})
}
if opts.WorkflowID != "" {
- cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID})
+ cond = cond.And(builder.Eq{"`action_run`.workflow_id": opts.WorkflowID})
}
if opts.TriggerUserID > 0 {
- cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
+ cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
}
if opts.Approved {
- cond = cond.And(builder.Gt{"approved_by": 0})
+ cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
}
if len(opts.Status) > 0 {
- cond = cond.And(builder.In("status", opts.Status))
+ cond = cond.And(builder.In("`action_run`.status", opts.Status))
}
if opts.Ref != "" {
- cond = cond.And(builder.Eq{"ref": opts.Ref})
+ cond = cond.And(builder.Eq{"`action_run`.ref": opts.Ref})
}
if opts.TriggerEvent != "" {
- cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
+ cond = cond.And(builder.Eq{"`action_run`.trigger_event": opts.TriggerEvent})
+ }
+ if opts.CommitSHA != "" {
+ cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
}
return cond
}
+func (opts FindRunOptions) ToJoins() []db.JoinFunc {
+ if opts.OwnerID > 0 {
+ return []db.JoinFunc{func(sess db.Engine) error {
+ sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
+ return nil
+ }}
+ }
+ return nil
+}
+
func (opts FindRunOptions) ToOrders() string {
- return "`id` DESC"
+ return "`action_run`.`id` DESC"
}
type StatusInfo struct {
@@ -112,14 +124,14 @@ type StatusInfo struct {
}
// GetStatusInfoList returns a slice of StatusInfo
-func GetStatusInfoList(ctx context.Context) []StatusInfo {
+func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
// same as those in aggregateJobStatus
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning}
statusInfoList := make([]StatusInfo, 0, 4)
for _, s := range allStatus {
statusInfoList = append(statusInfoList, StatusInfo{
Status: int(s),
- DisplayedStatus: s.String(),
+ DisplayedStatus: s.LocaleString(lang),
})
}
return statusInfoList
diff --git a/models/actions/runner.go b/models/actions/runner.go
index b35a76680c..81d4249ae0 100644
--- a/models/actions/runner.go
+++ b/models/actions/runner.go
@@ -5,6 +5,7 @@ package actions
import (
"context"
+ "errors"
"fmt"
"strings"
"time"
@@ -14,6 +15,7 @@ import (
"code.gitea.io/gitea/models/shared/types"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
@@ -57,6 +59,8 @@ type ActionRunner struct {
// Store labels defined in state file (default: .runner file) of `act_runner`
AgentLabels []string `xorm:"TEXT"`
+ // Store if this is a runner that only ever get one single job assigned
+ Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
@@ -84,9 +88,10 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
return types.OwnerTypeRepository
}
if r.OwnerID != 0 {
- if r.Owner.Type == user_model.UserTypeOrganization {
+ switch r.Owner.Type {
+ case user_model.UserTypeOrganization:
return types.OwnerTypeOrganization
- } else if r.Owner.Type == user_model.UserTypeIndividual {
+ case user_model.UserTypeIndividual:
return types.OwnerTypeIndividual
}
}
@@ -120,8 +125,15 @@ func (r *ActionRunner) IsOnline() bool {
return false
}
-// Editable checks if the runner is editable by the user
-func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
+// EditableInContext checks if the runner is editable by the "context" owner/repo
+// ownerID == 0 and repoID == 0 means "admin" context, any runner including global runners could be edited
+// ownerID == 0 and repoID != 0 means "repo" context, any runner belonging to the given repo could be edited
+// ownerID != 0 and repoID == 0 means "owner(org/user)" context, any runner belonging to the given user/org could be edited
+// ownerID != 0 and repoID != 0 means "owner" OR "repo" context, legacy behavior, but we should forbid using it
+func (r *ActionRunner) EditableInContext(ownerID, repoID int64) bool {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
if ownerID == 0 && repoID == 0 {
return true
}
@@ -165,8 +177,15 @@ func init() {
db.RegisterModel(&ActionRunner{})
}
+// FindRunnerOptions
+// ownerID == 0 and repoID == 0 means any runner including global runners
+// repoID != 0 and WithAvailable == false means any runner for the given repo
+// repoID != 0 and WithAvailable == true means any runner for the given repo, parent user/org, and global runners
+// ownerID != 0 and repoID == 0 and WithAvailable == false means any runner for the given user/org
+// ownerID != 0 and repoID == 0 and WithAvailable == true means any runner for the given user/org and global runners
type FindRunnerOptions struct {
db.ListOptions
+ IDs []int64
RepoID int64
OwnerID int64 // it will be ignored if RepoID is set
Sort string
@@ -178,6 +197,14 @@ type FindRunnerOptions struct {
func (opts FindRunnerOptions) ToConds() builder.Cond {
cond := builder.NewCond()
+ if len(opts.IDs) > 0 {
+ if len(opts.IDs) == 1 {
+ cond = cond.And(builder.Eq{"id": opts.IDs[0]})
+ } else {
+ cond = cond.And(builder.In("id", opts.IDs))
+ }
+ }
+
if opts.RepoID > 0 {
c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID})
if opts.WithAvailable {
@@ -252,7 +279,7 @@ func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) {
// UpdateRunner updates runner's information.
func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
e := db.GetEngine(ctx)
- r.Name, _ = util.SplitStringAtByteN(r.Name, 255)
+ r.Name = util.EllipsisDisplayString(r.Name, 255)
var err error
if len(cols) == 0 {
_, err = e.ID(r.ID).AllCols().Update(r)
@@ -272,6 +299,23 @@ func DeleteRunner(ctx context.Context, id int64) error {
return err
}
+// DeleteEphemeralRunner deletes a ephemeral runner by given ID.
+func DeleteEphemeralRunner(ctx context.Context, id int64) error {
+ runner, err := GetRunnerByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ return nil
+ }
+ return err
+ }
+ if !runner.Ephemeral {
+ return nil
+ }
+
+ _, err = db.DeleteByID[ActionRunner](ctx, id)
+ return err
+}
+
// CreateRunner creates new runner.
func CreateRunner(ctx context.Context, t *ActionRunner) error {
if t.OwnerID != 0 && t.RepoID != 0 {
@@ -279,7 +323,7 @@ func CreateRunner(ctx context.Context, t *ActionRunner) error {
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
t.OwnerID = 0
}
- t.Name, _ = util.SplitStringAtByteN(t.Name, 255)
+ t.Name = util.EllipsisDisplayString(t.Name, 255)
return db.Insert(ctx, t)
}
@@ -328,3 +372,17 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
}
return res.RowsAffected()
}
+
+func CountWrongRepoLevelRunners(ctx context.Context) (int64, error) {
+ var result int64
+ _, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_runner` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
+ return result, err
+}
+
+func UpdateWrongRepoLevelRunners(ctx context.Context) (int64, error) {
+ result, err := db.GetEngine(ctx).Exec("UPDATE `action_runner` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
diff --git a/models/actions/runner_token.go b/models/actions/runner_token.go
index fd6ba7ecad..bbd2af73b6 100644
--- a/models/actions/runner_token.go
+++ b/models/actions/runner_token.go
@@ -51,7 +51,7 @@ func GetRunnerToken(ctx context.Context, token string) (*ActionRunnerToken, erro
if err != nil {
return nil, err
} else if !has {
- return nil, fmt.Errorf("runner token %q: %w", token, util.ErrNotExist)
+ return nil, fmt.Errorf(`runner token "%s...": %w`, util.TruncateRunes(token, 3), util.ErrNotExist)
}
return &runnerToken, nil
}
@@ -68,19 +68,15 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string
return err
}
-// NewRunnerToken creates a new active runner token and invalidate all old tokens
+// NewRunnerTokenWithValue creates a new active runner token and invalidate all old tokens
// ownerID will be ignored and treated as 0 if repoID is non-zero.
-func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
+func NewRunnerTokenWithValue(ctx context.Context, ownerID, repoID int64, token string) (*ActionRunnerToken, error) {
if ownerID != 0 && repoID != 0 {
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
ownerID = 0
}
- token, err := util.CryptoRandomString(40)
- if err != nil {
- return nil, err
- }
runnerToken := &ActionRunnerToken{
OwnerID: ownerID,
RepoID: repoID,
@@ -95,11 +91,19 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerTo
return err
}
- _, err = db.GetEngine(ctx).Insert(runnerToken)
+ _, err := db.GetEngine(ctx).Insert(runnerToken)
return err
})
}
+func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
+ token, err := util.CryptoRandomString(40)
+ if err != nil {
+ return nil, err
+ }
+ return NewRunnerTokenWithValue(ctx, ownerID, repoID, token)
+}
+
// GetLatestRunnerToken returns the latest runner token
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
if ownerID != 0 && repoID != 0 {
diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go
index 159805e5f7..21614b7086 100644
--- a/models/actions/runner_token_test.go
+++ b/models/actions/runner_token_test.go
@@ -17,7 +17,7 @@ func TestGetLatestRunnerToken(t *testing.T) {
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
func TestNewRunnerToken(t *testing.T) {
@@ -26,7 +26,7 @@ func TestNewRunnerToken(t *testing.T) {
assert.NoError(t, err)
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
func TestUpdateRunnerToken(t *testing.T) {
@@ -36,5 +36,5 @@ func TestUpdateRunnerToken(t *testing.T) {
assert.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
diff --git a/models/actions/schedule.go b/models/actions/schedule.go
index 961ffd0851..2edf483fe0 100644
--- a/models/actions/schedule.go
+++ b/models/actions/schedule.go
@@ -43,15 +43,12 @@ func init() {
// GetSchedulesMapByIDs returns the schedules by given id slice.
func GetSchedulesMapByIDs(ctx context.Context, ids []int64) (map[int64]*ActionSchedule, error) {
schedules := make(map[int64]*ActionSchedule, len(ids))
+ if len(ids) == 0 {
+ return schedules, nil
+ }
return schedules, db.GetEngine(ctx).In("id", ids).Find(&schedules)
}
-// GetReposMapByIDs returns the repos by given id slice.
-func GetReposMapByIDs(ctx context.Context, ids []int64) (map[int64]*repo_model.Repository, error) {
- repos := make(map[int64]*repo_model.Repository, len(ids))
- return repos, db.GetEngine(ctx).In("id", ids).Find(&repos)
-}
-
// CreateScheduleTask creates new schedule task.
func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
// Return early if there are no rows to insert
@@ -68,7 +65,7 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
// Loop through each schedule row
for _, row := range rows {
- row.Title, _ = util.SplitStringAtByteN(row.Title, 255)
+ row.Title = util.EllipsisDisplayString(row.Title, 255)
// Create new schedule row
if err = db.Insert(ctx, row); err != nil {
return err
@@ -120,21 +117,22 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
return committer.Commit()
}
-func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
+func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
// There is no other place we can do this because the app.ini will be changed manually
if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
- return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
+ return nil, fmt.Errorf("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := CancelPreviousJobs(
+ jobs, err := CancelPreviousJobs(
ctx,
repo.ID,
repo.DefaultBranch,
"",
webhook_module.HookEventSchedule,
- ); err != nil {
- return fmt.Errorf("CancelPreviousJobs: %v", err)
+ )
+ if err != nil {
+ return jobs, fmt.Errorf("CancelPreviousJobs: %v", err)
}
- return nil
+ return jobs, nil
}
diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go
index f7dac72f8b..e26b2c1120 100644
--- a/models/actions/schedule_spec_list.go
+++ b/models/actions/schedule_spec_list.go
@@ -32,7 +32,7 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error {
}
repoIDs := specs.GetRepoIDs()
- repos, err := GetReposMapByIDs(ctx, repoIDs)
+ repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
if err != nil {
return err
}
diff --git a/models/actions/status.go b/models/actions/status.go
index eda2234137..2b1d70613c 100644
--- a/models/actions/status.go
+++ b/models/actions/status.go
@@ -4,6 +4,8 @@
package actions
import (
+ "slices"
+
"code.gitea.io/gitea/modules/translation"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
@@ -88,12 +90,7 @@ func (s Status) IsBlocked() bool {
// In returns whether s is one of the given statuses
func (s Status) In(statuses ...Status) bool {
- for _, v := range statuses {
- if s == v {
- return true
- }
- }
- return false
+ return slices.Contains(statuses, s)
}
func (s Status) AsResult() runnerv1.Result {
diff --git a/models/actions/task.go b/models/actions/task.go
index af74faf937..e0756b10c2 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -6,6 +6,7 @@ package actions
import (
"context"
"crypto/subtle"
+ "errors"
"fmt"
"time"
@@ -277,14 +278,13 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
return nil, false, err
}
- var workflowJob *jobparser.Job
- if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil {
+ parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
+ if err != nil {
return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err)
- } else if len(gots) != 1 {
+ } else if len(parsedWorkflows) != 1 {
return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID)
- } else { //nolint:revive
- _, workflowJob = gots[0].Job()
}
+ _, workflowJob := parsedWorkflows[0].Job()
if _, err := e.Insert(task); err != nil {
return nil, false, err
@@ -298,7 +298,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
if len(workflowJob.Steps) > 0 {
steps := make([]*ActionTaskStep, len(workflowJob.Steps))
for i, v := range workflowJob.Steps {
- name, _ := util.SplitStringAtByteN(v.String(), 255)
+ name := util.EllipsisDisplayString(v.String(), 255)
steps[i] = &ActionTaskStep{
Name: name,
TaskID: task.ID,
@@ -335,6 +335,11 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
sess.Cols(cols...)
}
_, err := sess.Update(task)
+
+ // Automatically delete the ephemeral runner if the task is done
+ if err == nil && task.Status.IsDone() && util.SliceContainsString(cols, "status") {
+ return DeleteEphemeralRunner(ctx, task.RunnerID)
+ }
return err
}
@@ -361,7 +366,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
} else if !has {
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID {
- return nil, fmt.Errorf("invalid runner for task")
+ return nil, errors.New("invalid runner for task")
}
if task.Status.IsDone() {
diff --git a/models/actions/task_list.go b/models/actions/task_list.go
index df4b43c5ef..0c80397899 100644
--- a/models/actions/task_list.go
+++ b/models/actions/task_list.go
@@ -48,6 +48,7 @@ func (tasks TaskList) LoadAttributes(ctx context.Context) error {
type FindTaskOptions struct {
db.ListOptions
RepoID int64
+ JobID int64
OwnerID int64
CommitSHA string
Status Status
@@ -61,6 +62,9 @@ func (opts FindTaskOptions) ToConds() builder.Cond {
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
+ if opts.JobID > 0 {
+ cond = cond.And(builder.Eq{"job_id": opts.JobID})
+ }
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
diff --git a/models/actions/utils.go b/models/actions/utils.go
index 12657942fc..f6ba661ae3 100644
--- a/models/actions/utils.go
+++ b/models/actions/utils.go
@@ -82,3 +82,22 @@ func calculateDuration(started, stopped timeutil.TimeStamp, status Status) time.
}
return timeSince(s).Truncate(time.Second)
}
+
+// best effort function to convert an action schedule to action run, to be used in GenerateGiteaContext
+func (s *ActionSchedule) ToActionRun() *ActionRun {
+ return &ActionRun{
+ Title: s.Title,
+ RepoID: s.RepoID,
+ Repo: s.Repo,
+ OwnerID: s.OwnerID,
+ WorkflowID: s.WorkflowID,
+ TriggerUserID: s.TriggerUserID,
+ TriggerUser: s.TriggerUser,
+ Ref: s.Ref,
+ CommitSHA: s.CommitSHA,
+ Event: s.Event,
+ EventPayload: s.EventPayload,
+ Created: s.Created,
+ Updated: s.Updated,
+ }
+}
diff --git a/models/actions/variable.go b/models/actions/variable.go
index d0f917d923..7154843c17 100644
--- a/models/actions/variable.go
+++ b/models/actions/variable.go
@@ -6,10 +6,12 @@ package actions
import (
"context"
"strings"
+ "unicode/utf8"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -32,32 +34,46 @@ type ActionVariable struct {
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
Data string `xorm:"LONGTEXT NOT NULL"`
+ Description string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
+const (
+ VariableDataMaxLength = 65536
+ VariableDescriptionMaxLength = 4096
+)
+
func init() {
db.RegisterModel(new(ActionVariable))
}
-func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*ActionVariable, error) {
+func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*ActionVariable, error) {
if ownerID != 0 && repoID != 0 {
// It's trying to create a variable that belongs to a repository, but OwnerID has been set accidentally.
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
ownerID = 0
}
+ if utf8.RuneCountInString(data) > VariableDataMaxLength {
+ return nil, util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ description = util.TruncateRunes(description, VariableDescriptionMaxLength)
+
variable := &ActionVariable{
- OwnerID: ownerID,
- RepoID: repoID,
- Name: strings.ToUpper(name),
- Data: data,
+ OwnerID: ownerID,
+ RepoID: repoID,
+ Name: strings.ToUpper(name),
+ Data: data,
+ Description: description,
}
return variable, db.Insert(ctx, variable)
}
type FindVariablesOpts struct {
db.ListOptions
+ IDs []int64
RepoID int64
OwnerID int64 // it will be ignored if RepoID is set
Name string
@@ -65,6 +81,15 @@ type FindVariablesOpts struct {
func (opts FindVariablesOpts) ToConds() builder.Cond {
cond := builder.NewCond()
+
+ if len(opts.IDs) > 0 {
+ if len(opts.IDs) == 1 {
+ cond = cond.And(builder.Eq{"id": opts.IDs[0]})
+ } else {
+ cond = cond.And(builder.In("id", opts.IDs))
+ }
+ }
+
// Since we now support instance-level variables,
// there is no need to check for null values for `owner_id` and `repo_id`
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -85,12 +110,18 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
return db.Find[ActionVariable](ctx, opts)
}
-func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
- count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data").
- Update(&ActionVariable{
- Name: variable.Name,
- Data: variable.Data,
- })
+func UpdateVariableCols(ctx context.Context, variable *ActionVariable, cols ...string) (bool, error) {
+ if utf8.RuneCountInString(variable.Data) > VariableDataMaxLength {
+ return false, util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ variable.Description = util.TruncateRunes(variable.Description, VariableDescriptionMaxLength)
+
+ variable.Name = strings.ToUpper(variable.Name)
+ count, err := db.GetEngine(ctx).
+ ID(variable.ID).
+ Cols(cols...).
+ Update(variable)
return count != 0, err
}
@@ -137,3 +168,17 @@ func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string,
return variables, nil
}
+
+func CountWrongRepoLevelVariables(ctx context.Context) (int64, error) {
+ var result int64
+ _, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_variable` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
+ return result, err
+}
+
+func UpdateWrongRepoLevelVariables(ctx context.Context) (int64, error) {
+ result, err := db.GetEngine(ctx).Exec("UPDATE `action_variable` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
diff --git a/models/activities/action.go b/models/activities/action.go
index 65d95fbe66..1a0dfe6412 100644
--- a/models/activities/action.go
+++ b/models/activities/action.go
@@ -9,6 +9,7 @@ import (
"fmt"
"net/url"
"path"
+ "slices"
"strconv"
"strings"
"time"
@@ -16,16 +17,14 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm/schemas"
@@ -72,9 +71,9 @@ func (at ActionType) String() string {
case ActionRenameRepo:
return "rename_repo"
case ActionStarRepo:
- return "star_repo"
+ return "star_repo" // will not displayed in feeds.tmpl
case ActionWatchRepo:
- return "watch_repo"
+ return "watch_repo" // will not displayed in feeds.tmpl
case ActionCommitRepo:
return "commit_repo"
case ActionCreateIssue:
@@ -127,12 +126,7 @@ func (at ActionType) String() string {
}
func (at ActionType) InActions(actions ...string) bool {
- for _, action := range actions {
- if action == at.String() {
- return true
- }
- }
- return false
+ return slices.Contains(actions, at.String())
}
// Action represents user operation type and other information to
@@ -174,7 +168,10 @@ func (a *Action) TableIndices() []*schemas.Index {
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
cuIndex.AddColumn("user_id", "is_deleted")
- indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
+ actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
+ actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
+
+ indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
return indices
}
@@ -190,7 +187,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
return
}
var err error
- a.ActUser, err = user_model.GetUserByID(ctx, a.ActUserID)
+ a.ActUser, err = user_model.GetPossibleUserByID(ctx, a.ActUserID)
if err == nil {
return
} else if user_model.IsErrUserNotExist(err) {
@@ -200,15 +197,13 @@ func (a *Action) LoadActUser(ctx context.Context) {
}
}
-func (a *Action) LoadRepo(ctx context.Context) {
+func (a *Action) LoadRepo(ctx context.Context) error {
if a.Repo != nil {
- return
+ return nil
}
var err error
a.Repo, err = repo_model.GetRepositoryByID(ctx, a.RepoID)
- if err != nil {
- log.Error("repo_model.GetRepositoryByID(%d): %v", a.RepoID, err)
- }
+ return err
}
// GetActFullName gets the action's user full name.
@@ -226,7 +221,7 @@ func (a *Action) GetActUserName(ctx context.Context) string {
// ShortActUserName gets the action's user name trimmed to max 20
// chars.
func (a *Action) ShortActUserName(ctx context.Context) string {
- return base.EllipsisString(a.GetActUserName(ctx), 20)
+ return util.EllipsisDisplayString(a.GetActUserName(ctx), 20)
}
// GetActDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
@@ -250,7 +245,7 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
// GetRepoUserName returns the name of the action repository owner.
func (a *Action) GetRepoUserName(ctx context.Context) string {
- a.LoadRepo(ctx)
+ _ = a.LoadRepo(ctx)
if a.Repo == nil {
return "(non-existing-repo)"
}
@@ -260,12 +255,12 @@ func (a *Action) GetRepoUserName(ctx context.Context) string {
// ShortRepoUserName returns the name of the action repository owner
// trimmed to max 20 chars.
func (a *Action) ShortRepoUserName(ctx context.Context) string {
- return base.EllipsisString(a.GetRepoUserName(ctx), 20)
+ return util.EllipsisDisplayString(a.GetRepoUserName(ctx), 20)
}
// GetRepoName returns the name of the action repository.
func (a *Action) GetRepoName(ctx context.Context) string {
- a.LoadRepo(ctx)
+ _ = a.LoadRepo(ctx)
if a.Repo == nil {
return "(non-existing-repo)"
}
@@ -275,7 +270,7 @@ func (a *Action) GetRepoName(ctx context.Context) string {
// ShortRepoName returns the name of the action repository
// trimmed to max 33 chars.
func (a *Action) ShortRepoName(ctx context.Context) string {
- return base.EllipsisString(a.GetRepoName(ctx), 33)
+ return util.EllipsisDisplayString(a.GetRepoName(ctx), 33)
}
// GetRepoPath returns the virtual path to the action repository.
@@ -355,7 +350,7 @@ func (a *Action) GetBranch() string {
// GetRefLink returns the action's ref link.
func (a *Action) GetRefLink(ctx context.Context) string {
- return git.RefURL(a.GetRepoLink(ctx), a.RefName)
+ return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
}
// GetTag returns the action's repository tag.
@@ -446,6 +441,7 @@ type GetFeedsOptions struct {
OnlyPerformedBy bool // only actions performed by requested user
IncludeDeleted bool // include deleted actions
Date string // the day we want activity for: YYYY-MM-DD
+ DontCount bool // do counting in GetFeeds
}
// ActivityReadable return whether doer can read activities of user
@@ -454,6 +450,24 @@ func ActivityReadable(user, doer *user_model.User) bool {
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
}
+func FeedDateCond(opts GetFeedsOptions) builder.Cond {
+ cond := builder.NewCond()
+ if opts.Date == "" {
+ return cond
+ }
+
+ dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
+ if err != nil {
+ log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
+ } else {
+ dateHigh := dateLow.Add(86399000000000) // 23h59m59s
+
+ cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
+ cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
+ }
+ return cond
+}
+
func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
cond := builder.NewCond()
@@ -511,8 +525,8 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
}
if opts.RequestedTeam != nil {
- env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(ctx, opts.RequestedTeam)
- teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
+ env := repo_model.AccessibleTeamReposEnv(organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
+ teamRepoIDs, err := env.RepoIDs(ctx)
if err != nil {
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
}
@@ -534,17 +548,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
cond = cond.And(builder.Eq{"is_deleted": false})
}
- if opts.Date != "" {
- dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
- if err != nil {
- log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
- } else {
- dateHigh := dateLow.Add(86399000000000) // 23h59m59s
-
- cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
- cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
- }
- }
+ cond = cond.And(FeedDateCond(opts))
return cond, nil
}
@@ -559,130 +563,6 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
return err
}
-// NotifyWatchers creates batch of actions for every watcher.
-// It could insert duplicate actions for a repository action, like this:
-// * Original action: UserID=1 (the real actor), ActUserID=1
-// * Organization action: UserID=100 (the repo's org), ActUserID=1
-// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
-func NotifyWatchers(ctx context.Context, actions ...*Action) error {
- var watchers []*repo_model.Watch
- var repo *repo_model.Repository
- var err error
- var permCode []bool
- var permIssue []bool
- var permPR []bool
-
- e := db.GetEngine(ctx)
-
- for _, act := range actions {
- repoChanged := repo == nil || repo.ID != act.RepoID
-
- if repoChanged {
- // Add feeds for user self and all watchers.
- watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
- if err != nil {
- return fmt.Errorf("get watchers: %w", err)
- }
- }
-
- // Add feed for actioner.
- act.UserID = act.ActUserID
- if _, err = e.Insert(act); err != nil {
- return fmt.Errorf("insert new actioner: %w", err)
- }
-
- if repoChanged {
- act.LoadRepo(ctx)
- repo = act.Repo
-
- // check repo owner exist.
- if err := act.Repo.LoadOwner(ctx); err != nil {
- return fmt.Errorf("can't get repo owner: %w", err)
- }
- } else if act.Repo == nil {
- act.Repo = repo
- }
-
- // Add feed for organization
- if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
- act.ID = 0
- act.UserID = act.Repo.Owner.ID
- if err = db.Insert(ctx, act); err != nil {
- return fmt.Errorf("insert new actioner: %w", err)
- }
- }
-
- if repoChanged {
- permCode = make([]bool, len(watchers))
- permIssue = make([]bool, len(watchers))
- permPR = make([]bool, len(watchers))
- for i, watcher := range watchers {
- user, err := user_model.GetUserByID(ctx, watcher.UserID)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- permCode[i] = perm.CanRead(unit.TypeCode)
- permIssue[i] = perm.CanRead(unit.TypeIssues)
- permPR[i] = perm.CanRead(unit.TypePullRequests)
- }
- }
-
- for i, watcher := range watchers {
- if act.ActUserID == watcher.UserID {
- continue
- }
- act.ID = 0
- act.UserID = watcher.UserID
- act.Repo.Units = nil
-
- switch act.OpType {
- case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
- if !permCode[i] {
- continue
- }
- case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
- if !permIssue[i] {
- continue
- }
- case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest, ActionAutoMergePullRequest:
- if !permPR[i] {
- continue
- }
- }
-
- if err = db.Insert(ctx, act); err != nil {
- return fmt.Errorf("insert new action: %w", err)
- }
- }
- }
- return nil
-}
-
-// NotifyWatchersActions creates batch of actions for every watcher.
-func NotifyWatchersActions(ctx context.Context, acts []*Action) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- for _, act := range acts {
- if err := NotifyWatchers(ctx, act); err != nil {
- return err
- }
- }
- return committer.Commit()
-}
-
// DeleteIssueActions delete all actions related with issueID
func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) error {
// delete actions assigned to this issue
diff --git a/models/activities/action_list.go b/models/activities/action_list.go
index 5f9acb8f2a..b52cf7ee49 100644
--- a/models/activities/action_list.go
+++ b/models/activities/action_list.go
@@ -5,6 +5,7 @@ package activities
import (
"context"
+ "errors"
"fmt"
"strconv"
@@ -205,12 +206,34 @@ func (actions ActionList) LoadIssues(ctx context.Context) error {
// GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
- return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
+ return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
- cond, err := ActivityQueryCondition(ctx, opts)
- if err != nil {
- return nil, 0, err
+ var err error
+ var cond builder.Cond
+ // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
+ if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
+ cond = builder.Eq{
+ "user_id": opts.RequestedUser.ID,
+ }.And(
+ FeedDateCond(opts),
+ )
+
+ if !opts.IncludeDeleted {
+ cond = cond.And(builder.Eq{"is_deleted": false})
+ }
+
+ if !opts.IncludePrivate {
+ cond = cond.And(builder.Eq{"is_private": false})
+ }
+ if opts.OnlyPerformedBy {
+ cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
+ }
+ } else {
+ cond, err = ActivityQueryCondition(ctx, opts)
+ if err != nil {
+ return nil, 0, err
+ }
}
actions := make([]*Action, 0, opts.PageSize)
@@ -221,7 +244,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
sess := db.GetEngine(ctx).Where(cond)
sess = db.SetSessionPagination(sess, &opts)
- count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
+ if opts.DontCount {
+ err = sess.Desc("`action`.created_unix").Find(&actions)
+ } else {
+ count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
+ }
if err != nil {
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
@@ -235,11 +262,13 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
}
- count, err = db.GetEngine(ctx).Where(cond).
- Table("action").
- Cols("`action`.id").Count()
- if err != nil {
- return nil, 0, fmt.Errorf("Count: %w", err)
+ if !opts.DontCount {
+ count, err = db.GetEngine(ctx).Where(cond).
+ Table("action").
+ Cols("`action`.id").Count()
+ if err != nil {
+ return nil, 0, fmt.Errorf("Count: %w", err)
+ }
}
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
@@ -253,3 +282,9 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return actions, count, nil
}
+
+func CountUserFeeds(ctx context.Context, userID int64) (int64, error) {
+ return db.GetEngine(ctx).Where("user_id = ?", userID).
+ And("is_deleted = ?", false).
+ Count(&Action{})
+}
diff --git a/models/activities/action_test.go b/models/activities/action_test.go
index 9cfe981656..ff311ac891 100644
--- a/models/activities/action_test.go
+++ b/models/activities/action_test.go
@@ -82,43 +82,6 @@ func TestActivityReadable(t *testing.T) {
}
}
-func TestNotifyWatchers(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- action := &activities_model.Action{
- ActUserID: 8,
- RepoID: 1,
- OpType: activities_model.ActionStarRepo,
- }
- assert.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
-
- // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 8,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 1,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 4,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 11,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
-}
-
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.Type.IsSQLite3() {
t.Skip("Test is only for SQLite database.")
@@ -167,7 +130,7 @@ func TestDeleteIssueActions(t *testing.T) {
// load an issue
issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4})
- assert.NotEqualValues(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
+ assert.NotEqual(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
// insert a comment
err := db.Insert(db.DefaultContext, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID})
diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go
index 0cbb91df3c..b47f5dc404 100644
--- a/models/activities/notification_list.go
+++ b/models/activities/notification_list.go
@@ -208,10 +208,7 @@ func (nl NotificationList) LoadRepos(ctx context.Context) (repo_model.Repository
repos := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Rows(new(repo_model.Repository))
@@ -282,10 +279,7 @@ func (nl NotificationList) LoadIssues(ctx context.Context) ([]int, error) {
issues := make(map[int64]*issues_model.Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(issues_model.Issue))
@@ -377,10 +371,7 @@ func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) {
users := make(map[int64]*user_model.User, len(userIDs))
left := len(userIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", userIDs[:limit]).
Rows(new(user_model.User))
@@ -428,10 +419,7 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
comments := make(map[int64]*issues_model.Comment, len(commentIDs))
left := len(commentIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", commentIDs[:limit]).
Rows(new(issues_model.Comment))
diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go
index 52f0eacba1..5d2a29bc36 100644
--- a/models/activities/notification_test.go
+++ b/models/activities/notification_test.go
@@ -44,11 +44,11 @@ func TestNotificationsForUser(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, notfs, 3) {
assert.EqualValues(t, 5, notfs[0].ID)
- assert.EqualValues(t, user.ID, notfs[0].UserID)
+ assert.Equal(t, user.ID, notfs[0].UserID)
assert.EqualValues(t, 4, notfs[1].ID)
- assert.EqualValues(t, user.ID, notfs[1].UserID)
+ assert.Equal(t, user.ID, notfs[1].UserID)
assert.EqualValues(t, 2, notfs[2].ID)
- assert.EqualValues(t, user.ID, notfs[2].UserID)
+ assert.Equal(t, user.ID, notfs[2].UserID)
}
}
@@ -58,7 +58,7 @@ func TestNotification_GetRepo(t *testing.T) {
repo, err := notf.GetRepo(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, repo, notf.Repository)
- assert.EqualValues(t, notf.RepoID, repo.ID)
+ assert.Equal(t, notf.RepoID, repo.ID)
}
func TestNotification_GetIssue(t *testing.T) {
@@ -67,7 +67,7 @@ func TestNotification_GetIssue(t *testing.T) {
issue, err := notf.GetIssue(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, issue, notf.Issue)
- assert.EqualValues(t, notf.IssueID, issue.ID)
+ assert.Equal(t, notf.IssueID, issue.ID)
}
func TestGetNotificationCount(t *testing.T) {
@@ -136,5 +136,5 @@ func TestSetIssueReadBy(t *testing.T) {
nt, err := activities_model.GetIssueNotification(db.DefaultContext, user.ID, issue.ID)
assert.NoError(t, err)
- assert.EqualValues(t, activities_model.NotificationStatusRead, nt.Status)
+ assert.Equal(t, activities_model.NotificationStatusRead, nt.Status)
}
diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go
index 3ffad035b7..aeaa452c9e 100644
--- a/models/activities/repo_activity.go
+++ b/models/activities/repo_activity.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
+ "xorm.io/builder"
"xorm.io/xorm"
)
@@ -138,10 +139,7 @@ func GetActivityStatsTopAuthors(ctx context.Context, repo *repo_model.Repository
return v[i].Commits > v[j].Commits
})
- cnt := count
- if cnt > len(v) {
- cnt = len(v)
- }
+ cnt := min(count, len(v))
return v[:cnt], nil
}
@@ -337,8 +335,10 @@ func newlyCreatedIssues(ctx context.Context, repoID int64, fromTime time.Time) *
func activeIssues(ctx context.Context, repoID int64, fromTime time.Time) *xorm.Session {
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
And("issue.is_pull = ?", false).
- And("issue.created_unix >= ?", fromTime.Unix()).
- Or("issue.closed_unix >= ?", fromTime.Unix())
+ And(builder.Or(
+ builder.Gte{"issue.created_unix": fromTime.Unix()},
+ builder.Gte{"issue.closed_unix": fromTime.Unix()},
+ ))
return sess
}
diff --git a/models/activities/statistic.go b/models/activities/statistic.go
index ff81ad78a1..940651d359 100644
--- a/models/activities/statistic.go
+++ b/models/activities/statistic.go
@@ -17,13 +17,16 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
)
// Statistic contains the database statistics
type Statistic struct {
Counter struct {
- User, Org, PublicKey,
+ UsersActive, UsersNotActive,
+ Org, PublicKey,
Repo, Watch, Star, Access,
Issue, IssueClosed, IssueOpen,
Comment, Oauth, Follow,
@@ -53,8 +56,20 @@ type IssueByRepositoryCount struct {
// GetStatistic returns the database statistics
func GetStatistic(ctx context.Context) (stats Statistic) {
e := db.GetEngine(ctx)
- stats.Counter.User = user_model.CountUsers(ctx, nil)
- stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true})
+
+ // Number of active users
+ usersActiveOpts := user_model.CountUserFilter{
+ IsActive: optional.Some(true),
+ }
+ stats.Counter.UsersActive = user_model.CountUsers(ctx, &usersActiveOpts)
+
+ // Number of inactive users
+ usersNotActiveOpts := user_model.CountUserFilter{
+ IsActive: optional.Some(false),
+ }
+ stats.Counter.UsersNotActive = user_model.CountUsers(ctx, &usersNotActiveOpts)
+
+ stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludeVisibility: structs.VisibleTypePrivate})
stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{})
stats.Counter.Watch, _ = e.Count(new(repo_model.Watch))
diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go
index 1f8f0f590e..ef67838be7 100644
--- a/models/activities/user_heatmap.go
+++ b/models/activities/user_heatmap.go
@@ -66,7 +66,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
Select(groupBy+" AS timestamp, count(user_id) as contributions").
Table("action").
Where(cond).
- And("created_unix > ?", timeutil.TimeStampNow()-31536000).
+ And("created_unix > ?", timeutil.TimeStampNow()-(366+7)*86400). // (366+7) days to include the first week for the heatmap
GroupBy(groupByName).
OrderBy("timestamp").
Find(&hdata)
diff --git a/models/activities/user_heatmap_test.go b/models/activities/user_heatmap_test.go
index a039fd3613..380045d3c5 100644
--- a/models/activities/user_heatmap_test.go
+++ b/models/activities/user_heatmap_test.go
@@ -64,11 +64,9 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
for _, tc := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
- doer := &user_model.User{ID: tc.doerID}
- _, err := unittest.LoadBeanIfExists(doer)
- assert.NoError(t, err)
- if tc.doerID == 0 {
- doer = nil
+ var doer *user_model.User
+ if tc.doerID != 0 {
+ doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
}
// get the action for comparison
diff --git a/models/admin/task.go b/models/admin/task.go
index 10f8e6d570..0541a8ec78 100644
--- a/models/admin/task.go
+++ b/models/admin/task.go
@@ -44,7 +44,7 @@ func init() {
// TranslatableMessage represents JSON struct that can be translated with a Locale
type TranslatableMessage struct {
Format string
- Args []any `json:"omitempty"`
+ Args []any `json:",omitempty"`
}
// LoadRepo loads repository of the task
diff --git a/models/asymkey/error.go b/models/asymkey/error.go
index 03bc82302f..b765624579 100644
--- a/models/asymkey/error.go
+++ b/models/asymkey/error.go
@@ -25,7 +25,7 @@ func (err ErrKeyUnableVerify) Error() string {
}
// ErrKeyIsPrivate is returned when the provided key is a private key not a public key
-var ErrKeyIsPrivate = util.NewSilentWrapErrorf(util.ErrInvalidArgument, "the provided key is a private key")
+var ErrKeyIsPrivate = util.ErrorWrap(util.ErrInvalidArgument, "the provided key is a private key")
// ErrKeyNotExist represents a "KeyNotExist" kind of error.
type ErrKeyNotExist struct {
@@ -132,7 +132,7 @@ func IsErrGPGKeyParsing(err error) bool {
}
func (err ErrGPGKeyParsing) Error() string {
- return fmt.Sprintf("failed to parse gpg key %s", err.ParseError.Error())
+ return "failed to parse gpg key " + err.ParseError.Error()
}
// ErrGPGKeyNotExist represents a "GPGKeyNotExist" kind of error.
@@ -217,6 +217,7 @@ func (err ErrGPGKeyAccessDenied) Unwrap() error {
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
type ErrKeyAccessDenied struct {
UserID int64
+ RepoID int64
KeyID int64
Note string
}
@@ -228,8 +229,8 @@ func IsErrKeyAccessDenied(err error) bool {
}
func (err ErrKeyAccessDenied) Error() string {
- return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]",
- err.UserID, err.KeyID, err.Note)
+ return fmt.Sprintf("user does not have access to the key [user_id: %d, repo_id: %d, key_id: %d, note: %s]",
+ err.UserID, err.RepoID, err.KeyID, err.Note)
}
func (err ErrKeyAccessDenied) Unwrap() error {
diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go
index 5236b2d450..220f46ad1d 100644
--- a/models/asymkey/gpg_key.go
+++ b/models/asymkey/gpg_key.go
@@ -5,6 +5,7 @@ package asymkey
import (
"context"
+ "errors"
"fmt"
"strings"
"time"
@@ -13,8 +14,8 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"xorm.io/builder"
)
@@ -106,7 +107,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
if err != nil {
return nil, err
}
- keys, err := checkArmoredGPGKeyString(impKey.Content)
+ keys, err := CheckArmoredGPGKeyString(impKey.Content)
if err != nil {
return nil, err
}
@@ -115,7 +116,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
// parseSubGPGKey parse a sub Key
func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -141,7 +142,11 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// Parse Subkeys
subkeys := make([]*GPGKey, len(e.Subkeys))
for i, k := range e.Subkeys {
- subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, expiry)
+ subkeyExpiry := expiry
+ if k.Sig.KeyLifetimeSecs != nil {
+ subkeyExpiry = k.PublicKey.CreationTime.Add(time.Duration(*k.Sig.KeyLifetimeSecs) * time.Second)
+ }
+ subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, subkeyExpiry)
if err != nil {
return nil, ErrGPGKeyParsing{ParseError: err}
}
@@ -156,7 +161,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
emails := make([]*user_model.EmailAddress, 0, len(e.Identities))
for _, ident := range e.Identities {
- if ident.Revocation != nil {
+ if ident.Revoked(time.Now()) {
continue
}
email := strings.ToLower(strings.TrimSpace(ident.UserId.Email))
@@ -179,7 +184,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
}
}
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -203,7 +208,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// deleteGPGKey does the actual key deletion
func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
if keyID == "" {
- return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure
+ return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
}
// Delete imported key
n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport))
@@ -236,32 +241,9 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
return committer.Commit()
}
-func checkKeyEmails(ctx context.Context, email string, keys ...*GPGKey) (bool, string) {
- uid := int64(0)
- var userEmails []*user_model.EmailAddress
- var user *user_model.User
- for _, key := range keys {
- for _, e := range key.Emails {
- if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
- return true, e.Email
- }
- }
- if key.Verified && key.OwnerID != 0 {
- if uid != key.OwnerID {
- userEmails, _ = user_model.GetEmailAddresses(ctx, key.OwnerID)
- uid = key.OwnerID
- user = &user_model.User{ID: uid}
- _, _ = user_model.GetUser(ctx, user)
- }
- for _, e := range userEmails {
- if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
- return true, e.Email
- }
- }
- if user.KeepEmailPrivate && strings.EqualFold(email, user.GetEmail()) {
- return true, user.GetEmail()
- }
- }
- }
- return false, email
+func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
+ return db.Find[GPGKey](ctx, FindGPGKeyOptions{
+ KeyID: keyID,
+ IncludeSubKeys: true,
+ })
}
diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go
index 11124b1366..1c7d2c1da2 100644
--- a/models/asymkey/gpg_key_add.go
+++ b/models/asymkey/gpg_key_add.go
@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
- "github.com/keybase/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp"
)
// __________________ ________ ____ __.
@@ -67,7 +67,7 @@ func addGPGSubKey(ctx context.Context, key *GPGKey) (err error) {
// AddGPGKey adds new public key to database.
func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature string) ([]*GPGKey, error) {
- ekeys, err := checkArmoredGPGKeyString(content)
+ ekeys, err := CheckArmoredGPGKeyString(content)
if err != nil {
return nil, err
}
@@ -83,15 +83,15 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str
verified := false
// Handle provided signature
if signature != "" {
- signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature))
+ signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil)
if err != nil {
- signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature))
+ signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil)
}
if err != nil {
- signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature))
+ signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
}
if err != nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err)
return nil, ErrGPGInvalidTokenSignature{
ID: ekeys[0].PrimaryKey.KeyIdString(),
Wrapped: err,
diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go
index 26fad3bb3f..39ec893606 100644
--- a/models/asymkey/gpg_key_commit_verification.go
+++ b/models/asymkey/gpg_key_commit_verification.go
@@ -4,19 +4,15 @@
package asymkey
import (
- "context"
+ "errors"
"fmt"
"hash"
- "strings"
- "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
)
// __________________ ________ ____ __.
@@ -70,267 +66,10 @@ const (
NoKeyFound = "gpg.error.no_gpg_keys_found"
)
-// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
-func ParseCommitsWithSignature(ctx context.Context, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error)) []*SignCommit {
- newCommits := make([]*SignCommit, 0, len(oldCommits))
- keyMap := map[string]bool{}
-
- for _, c := range oldCommits {
- signCommit := &SignCommit{
- UserCommit: c,
- Verification: ParseCommitWithSignature(ctx, c.Commit),
- }
-
- _ = CalculateTrustStatus(signCommit.Verification, repoTrustModel, isOwnerMemberCollaborator, &keyMap)
-
- newCommits = append(newCommits, signCommit)
- }
- return newCommits
-}
-
-// ParseCommitWithSignature check if signature is good against keystore.
-func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerification {
- var committer *user_model.User
- if c.Committer != nil {
- var err error
- // Find Committer account
- committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not
- if err != nil { // Skipping not user for committer
- committer = &user_model.User{
- Name: c.Committer.Name,
- Email: c.Committer.Email,
- }
- // We can expect this to often be an ErrUserNotExist. in the case
- // it is not, however, it is important to log it.
- if !user_model.IsErrUserNotExist(err) {
- log.Error("GetUserByEmail: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.no_committer_account",
- }
- }
- }
- }
-
- // If no signature just report the committer
- if c.Signature == nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false, // Default value
- Reason: "gpg.error.not_signed_commit", // Default value
- }
- }
-
- // If this a SSH signature handle it differently
- if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") {
- return ParseCommitWithSSHSignature(ctx, c, committer)
- }
-
- // Parsing signature
- sig, err := extractSignature(c.Signature.Signature)
- if err != nil { // Skipping failed to extract sign
- log.Error("SignatureRead err: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.extract_sign",
- }
- }
-
- keyID := tryGetKeyIDFromSignature(sig)
- defaultReason := NoKeyFound
-
- // First check if the sig has a keyID and if so just look at that
- if commitVerification := hashAndVerifyForKeyID(
- ctx,
- sig,
- c.Signature.Payload,
- committer,
- keyID,
- setting.AppName,
- ""); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
-
- // Now try to associate the signature with the committer, if present
- if committer.ID != 0 {
- keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
- OwnerID: committer.ID,
- })
- if err != nil { // Skipping failed to get gpg keys of user
- log.Error("ListGPGKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- if err := GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
- log.Error("LoadSubKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
- activated := false
- for _, e := range committerEmailAddresses {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- activated = true
- break
- }
- }
-
- for _, k := range keys {
- // Pre-check (& optimization) that emails attached to key can be attached to the committer email and can validate
- canValidate := false
- email := ""
- if k.Verified && activated {
- canValidate = true
- email = c.Committer.Email
- }
- if !canValidate {
- for _, e := range k.Emails {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- canValidate = true
- email = e.Email
- break
- }
- }
- }
- if !canValidate {
- continue // Skip this key
- }
-
- commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, c.Signature.Payload, k, committer, committer, email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- }
-
- if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
- // OK we should try the default key
- gpgSettings := git.GPGSettings{
- Sign: true,
- KeyID: setting.Repository.Signing.SigningKey,
- Name: setting.Repository.Signing.SigningName,
- Email: setting.Repository.Signing.SigningEmail,
- }
- if err := gpgSettings.LoadPublicKeyContent(); err != nil {
- log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
- } else if commitVerification := verifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
- }
-
- defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
- if err != nil {
- log.Error("Error getting default public gpg key: %v", err)
- } else if defaultGPGSettings == nil {
- log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
- } else if defaultGPGSettings.Sign {
- if commitVerification := verifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
- }
-
- return &CommitVerification{ // Default at this stage
- CommittingUser: committer,
- Verified: false,
- Warning: defaultReason != NoKeyFound,
- Reason: defaultReason,
- SigningKey: &GPGKey{
- KeyID: keyID,
- },
- }
-}
-
-func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *CommitVerification {
- // First try to find the key in the db
- if commitVerification := hashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
- return commitVerification
- }
-
- // Otherwise we have to parse the key
- ekeys, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent)
- if err != nil {
- log.Error("Unable to get default signing key: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- for _, ekey := range ekeys {
- pubkey := ekey.PrimaryKey
- content, err := base64EncPubKey(pubkey)
- if err != nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- k := &GPGKey{
- Content: content,
- CanSign: pubkey.CanSign(),
- KeyID: pubkey.KeyIdString(),
- }
- for _, subKey := range ekey.Subkeys {
- content, err := base64EncPubKey(subKey.PublicKey)
- if err != nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- k.SubsKey = append(k.SubsKey, &GPGKey{
- Content: content,
- CanSign: subKey.PublicKey.CanSign(),
- KeyID: subKey.PublicKey.KeyIdString(),
- })
- }
- if commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &user_model.User{
- Name: gpgSettings.Name,
- Email: gpgSettings.Email,
- }, gpgSettings.Email); commitVerification != nil {
- return commitVerification
- }
- if keyID == k.KeyID {
- // This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Warning: true,
- Reason: BadSignature,
- }
- }
- }
- return nil
-}
-
func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
// Check if key can sign
if !k.CanSign {
- return fmt.Errorf("key can not sign")
+ return errors.New("key can not sign")
}
// Decode key
pkey, err := base64DecPubKey(k.Content)
@@ -369,7 +108,7 @@ func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey)
return nil, nil
}
-func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
+func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
key, err := hashAndVerifyWithSubKeys(sig, payload, k)
if err != nil { // Skipping failed to generate hash
return &CommitVerification{
@@ -392,78 +131,6 @@ func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload s
return nil
}
-func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload string, committer *user_model.User, keyID, name, email string) *CommitVerification {
- if keyID == "" {
- return nil
- }
- keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
- KeyID: keyID,
- IncludeSubKeys: true,
- })
- if err != nil {
- log.Error("GetGPGKeysByKeyID: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
- if len(keys) == 0 {
- return nil
- }
- for _, key := range keys {
- var primaryKeys []*GPGKey
- if key.PrimaryKeyID != "" {
- primaryKeys, err = db.Find[GPGKey](ctx, FindGPGKeyOptions{
- KeyID: key.PrimaryKeyID,
- IncludeSubKeys: true,
- })
- if err != nil {
- log.Error("GetGPGKeysByKeyID: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
- }
-
- activated, email := checkKeyEmails(ctx, email, append([]*GPGKey{key}, primaryKeys...)...)
- if !activated {
- continue
- }
-
- signer := &user_model.User{
- Name: name,
- Email: email,
- }
- if key.OwnerID != 0 {
- owner, err := user_model.GetUserByID(ctx, key.OwnerID)
- if err == nil {
- signer = owner
- } else if !user_model.IsErrUserNotExist(err) {
- log.Error("Failed to user_model.GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.no_committer_account",
- }
- }
- }
- commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, key, committer, signer, email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- // This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Warning: true,
- Reason: BadSignature,
- }
-}
-
// CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
// There are several trust models in Gitea
func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) error {
diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go
index 28cb8f4e76..76f52a3ca4 100644
--- a/models/asymkey/gpg_key_common.go
+++ b/models/asymkey/gpg_key_common.go
@@ -7,15 +7,16 @@ import (
"bytes"
"crypto"
"encoding/base64"
+ "errors"
"fmt"
"hash"
"io"
"strings"
"time"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
)
// __________________ ________ ____ __.
@@ -33,9 +34,9 @@ import (
// This file provides common functions relating to GPG Keys
-// checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
+// CheckArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
// The function returns the actual public key on success
-func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
+func CheckArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content))
if err != nil {
return nil, ErrGPGKeyParsing{err}
@@ -43,8 +44,8 @@ func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
return list, nil
}
-// base64EncPubKey encode public key content to base 64
-func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
+// Base64EncPubKey encode public key content to base 64
+func Base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
var w bytes.Buffer
err := pubkey.Serialize(&w)
if err != nil {
@@ -75,12 +76,12 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
// Check type
pkey, ok := p.(*packet.PublicKey)
if !ok {
- return nil, fmt.Errorf("key is not a public key")
+ return nil, errors.New("key is not a public key")
}
return pkey, nil
}
-// getExpiryTime extract the expire time of primary key based on sig
+// getExpiryTime extract the expiry time of primary key based on sig
func getExpiryTime(e *openpgp.Entity) time.Time {
expiry := time.Time{}
// Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
@@ -88,12 +89,12 @@ func getExpiryTime(e *openpgp.Entity) time.Time {
for _, ident := range e.Identities {
if selfSig == nil {
selfSig = ident.SelfSignature
- } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
+ } else if ident.SelfSignature != nil && ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
selfSig = ident.SelfSignature
break
}
}
- if selfSig.KeyLifetimeSecs != nil {
+ if selfSig != nil && selfSig.KeyLifetimeSecs != nil {
expiry = e.PrimaryKey.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
}
return expiry
@@ -119,23 +120,23 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
return block.Body, nil
}
-func extractSignature(s string) (*packet.Signature, error) {
+func ExtractSignature(s string) (*packet.Signature, error) {
r, err := readArmoredSign(strings.NewReader(s))
if err != nil {
- return nil, fmt.Errorf("Failed to read signature armor")
+ return nil, errors.New("Failed to read signature armor")
}
p, err := packet.Read(r)
if err != nil {
- return nil, fmt.Errorf("Failed to read signature packet")
+ return nil, errors.New("Failed to read signature packet")
}
sig, ok := p.(*packet.Signature)
if !ok {
- return nil, fmt.Errorf("Packet is not a signature")
+ return nil, errors.New("Packet is not a signature")
}
return sig, nil
}
-func tryGetKeyIDFromSignature(sig *packet.Signature) string {
+func TryGetKeyIDFromSignature(sig *packet.Signature) string {
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
}
diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go
index d3fbb01d82..408cf15763 100644
--- a/models/asymkey/gpg_key_test.go
+++ b/models/asymkey/gpg_key_test.go
@@ -13,8 +13,10 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestCheckArmoredGPGKeyString(t *testing.T) {
@@ -49,7 +51,7 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
=i9b7
-----END PGP PUBLIC KEY BLOCK-----`
- key, err := checkArmoredGPGKeyString(testGPGArmor)
+ key, err := CheckArmoredGPGKeyString(testGPGArmor)
assert.NoError(t, err, "Could not parse a valid GPG public armored rsa key", key)
// TODO verify value of key
}
@@ -70,7 +72,7 @@ OyjLLnFQiVmq7kEA/0z0CQe3ZQiQIq5zrs7Nh1XRkFAo8GlU/SGC9XFFi722
=ZiSe
-----END PGP PUBLIC KEY BLOCK-----`
- key, err := checkArmoredGPGKeyString(testGPGArmor)
+ key, err := CheckArmoredGPGKeyString(testGPGArmor)
assert.NoError(t, err, "Could not parse a valid GPG public armored brainpoolP256r1 key", key)
// TODO verify value of key
}
@@ -106,15 +108,14 @@ Av844q/BfRuVsJsK1NDNG09LC30B0l3LKBqlrRmRTUMHtgchdX2dY+p7GPOoSzlR
MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
=i9b7
-----END PGP PUBLIC KEY BLOCK-----`
- keys, err := checkArmoredGPGKeyString(testGPGArmor)
- if !assert.NotEmpty(t, keys) {
- return
- }
+ keys, err := CheckArmoredGPGKeyString(testGPGArmor)
+ require.NotEmpty(t, keys)
+
ekey := keys[0]
assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey)
pubkey := ekey.PrimaryKey
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
assert.NoError(t, err, "Could not base64 encode a valid PublicKey content", ekey)
key := &GPGKey{
@@ -175,9 +176,9 @@ committer Antoine GIRARD 1489013107 +0100
Unknown GPG key with good email
`
// Reading Sign
- goodSig, err := extractSignature(testGoodSigArmor)
+ goodSig, err := ExtractSignature(testGoodSigArmor)
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testGoodSigArmor)
- badSig, err := extractSignature(testBadSigArmor)
+ badSig, err := ExtractSignature(testBadSigArmor)
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testBadSigArmor)
// Generating hash of commit
@@ -385,7 +386,7 @@ epiDVQ==
=VSKJ
-----END PGP PUBLIC KEY BLOCK-----
`
- keys, err := checkArmoredGPGKeyString(testIssue6599)
+ keys, err := CheckArmoredGPGKeyString(testIssue6599)
assert.NoError(t, err)
if assert.NotEmpty(t, keys) {
ekey := keys[0]
@@ -395,11 +396,33 @@ epiDVQ==
}
func TestTryGetKeyIDFromSignature(t *testing.T) {
- assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{}))
- assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+ assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{}))
+ assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
}))
- assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+ assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
}))
}
+
+func TestParseGPGKey(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ assert.NoError(t, db.Insert(db.DefaultContext, &user_model.EmailAddress{UID: 1, Email: "email1@example.com", IsActivated: true}))
+
+ // create a key for test email
+ e, err := openpgp.NewEntity("name", "comment", "email1@example.com", nil)
+ require.NoError(t, err)
+ k, err := parseGPGKey(db.DefaultContext, 1, e, true)
+ require.NoError(t, err)
+ assert.NotEmpty(t, k.KeyID)
+ assert.NotEmpty(t, k.Emails) // the key is valid, matches the email
+
+ // then revoke the key
+ for _, id := range e.Identities {
+ id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: util.ToPointer(packet.KeyCompromised)})
+ }
+ k, err = parseGPGKey(db.DefaultContext, 1, e, true)
+ require.NoError(t, err)
+ assert.NotEmpty(t, k.KeyID)
+ assert.Empty(t, k.Emails) // the key is revoked, matches no email
+}
diff --git a/models/asymkey/gpg_key_verify.go b/models/asymkey/gpg_key_verify.go
index 01812a2d54..5ab2fd8081 100644
--- a/models/asymkey/gpg_key_verify.go
+++ b/models/asymkey/gpg_key_verify.go
@@ -50,7 +50,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
return "", err
}
- sig, err := extractSignature(signature)
+ sig, err := ExtractSignature(signature)
if err != nil {
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
@@ -85,7 +85,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
}
if signer == nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ log.Debug("VerifyGPGKey failed: no signer")
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
}
diff --git a/models/asymkey/ssh_key_commit_verification.go b/models/asymkey/ssh_key_commit_verification.go
deleted file mode 100644
index 27c6df3578..0000000000
--- a/models/asymkey/ssh_key_commit_verification.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package asymkey
-
-import (
- "bytes"
- "context"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
-
- "github.com/42wim/sshsig"
-)
-
-// ParseCommitWithSSHSignature check if signature is good against keystore.
-func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
- // Now try to associate the signature with the committer, if present
- if committer.ID != 0 {
- keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{
- OwnerID: committer.ID,
- NotKeytype: KeyTypePrincipal,
- })
- if err != nil { // Skipping failed to get ssh keys of user
- log.Error("ListPublicKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- committerEmailAddresses, err := user_model.GetEmailAddresses(ctx, committer.ID)
- if err != nil {
- log.Error("GetEmailAddresses: %v", err)
- }
-
- activated := false
- for _, e := range committerEmailAddresses {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- activated = true
- break
- }
- }
-
- for _, k := range keys {
- if k.Verified && activated {
- commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committer, committer, c.Committer.Email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- }
- }
-
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: NoKeyFound,
- }
-}
-
-func verifySSHCommitVerification(sig, payload string, k *PublicKey, committer, signer *user_model.User, email string) *CommitVerification {
- if err := sshsig.Verify(bytes.NewBuffer([]byte(payload)), []byte(sig), []byte(k.Content), "git"); err != nil {
- return nil
- }
-
- return &CommitVerification{ // Everything is ok
- CommittingUser: committer,
- Verified: true,
- Reason: fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
- SigningUser: signer,
- SigningSSHKey: k,
- SigningEmail: email,
- }
-}
diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go
index 1ed3b5df2a..4dcfe1f279 100644
--- a/models/asymkey/ssh_key_fingerprint.go
+++ b/models/asymkey/ssh_key_fingerprint.go
@@ -6,27 +6,13 @@ package asymkey
import (
"context"
"fmt"
- "strings"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
)
-// ___________.__ .__ __
-// \_ _____/|__| ____ ____ ________________________|__| _____/ |_
-// | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
-// | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
-// \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
-// \/ \//_____/ \/ |__| \/
-//
-// This file contains functions for fingerprinting SSH keys
-//
// The database is used in checkKeyFingerprint however most of these functions probably belong in a module
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
@@ -41,29 +27,6 @@ func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
return nil
}
-func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
- // Calculate fingerprint.
- tmpPath, err := writeTmpKeyFile(publicKeyContent)
- if err != nil {
- return "", err
- }
- defer func() {
- if err := util.Remove(tmpPath); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
- }
- }()
- stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
- if err != nil {
- if strings.Contains(stderr, "is not a public key file") {
- return "", ErrKeyUnableVerify{stderr}
- }
- return "", util.NewInvalidArgumentErrorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
- } else if len(stdout) < 2 {
- return "", util.NewInvalidArgumentErrorf("not enough output for calculating fingerprint: %s", stdout)
- }
- return strings.Split(stdout, " ")[1], nil
-}
-
func calcFingerprintNative(publicKeyContent string) (string, error) {
// Calculate fingerprint.
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
@@ -75,15 +38,12 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint(publicKeyContent string) (string, error) {
- // Call the method based on configuration
- useNative := setting.SSH.KeygenPath == ""
- calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
- fp, err := calcFn(publicKeyContent)
+ fp, err := calcFingerprintNative(publicKeyContent)
if err != nil {
if IsErrKeyUnableVerify(err) {
return "", err
}
- return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
+ return "", fmt.Errorf("CalcFingerprint: %w", err)
}
return fp, nil
}
diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go
index 94b1cf112b..fc39f28624 100644
--- a/models/asymkey/ssh_key_parse.go
+++ b/models/asymkey/ssh_key_parse.go
@@ -10,14 +10,12 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/pem"
+ "errors"
"fmt"
"math/big"
- "os"
- "strconv"
"strings"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -93,7 +91,7 @@ func parseKeyString(content string) (string, error) {
block, _ := pem.Decode([]byte(content))
if block == nil {
- return "", fmt.Errorf("failed to parse PEM block containing the public key")
+ return "", errors.New("failed to parse PEM block containing the public key")
}
if strings.Contains(block.Type, "PRIVATE") {
return "", ErrKeyIsPrivate
@@ -174,20 +172,9 @@ func CheckPublicKeyString(content string) (_ string, err error) {
return content, nil
}
- var (
- fnName string
- keyType string
- length int
- )
- if len(setting.SSH.KeygenPath) == 0 {
- fnName = "SSHNativeParsePublicKey"
- keyType, length, err = SSHNativeParsePublicKey(content)
- } else {
- fnName = "SSHKeyGenParsePublicKey"
- keyType, length, err = SSHKeyGenParsePublicKey(content)
- }
+ keyType, length, err := SSHNativeParsePublicKey(content)
if err != nil {
- return "", fmt.Errorf("%s: %w", fnName, err)
+ return "", fmt.Errorf("SSHNativeParsePublicKey: %w", err)
}
log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
@@ -221,7 +208,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
// The ssh library can parse the key, so next we find out what key exactly we have.
switch pkey.Type() {
- case ssh.KeyAlgoDSA:
+ case ssh.KeyAlgoDSA: //nolint:staticcheck // it's deprecated
rawPub := struct {
Name string
P, Q, G, Y *big.Int
@@ -257,56 +244,3 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
}
return "", 0, fmt.Errorf("unsupported key length detection for type: %s", pkey.Type())
}
-
-// writeTmpKeyFile writes key content to a temporary file
-// and returns the name of that file, along with any possible errors.
-func writeTmpKeyFile(content string) (string, error) {
- tmpFile, err := os.CreateTemp(setting.SSH.KeyTestPath, "gitea_keytest")
- if err != nil {
- return "", fmt.Errorf("TempFile: %w", err)
- }
- defer tmpFile.Close()
-
- if _, err = tmpFile.WriteString(content); err != nil {
- return "", fmt.Errorf("WriteString: %w", err)
- }
- return tmpFile.Name(), nil
-}
-
-// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
-func SSHKeyGenParsePublicKey(key string) (string, int, error) {
- tmpName, err := writeTmpKeyFile(key)
- if err != nil {
- return "", 0, fmt.Errorf("writeTmpKeyFile: %w", err)
- }
- defer func() {
- if err := util.Remove(tmpName); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpName, err)
- }
- }()
-
- keygenPath := setting.SSH.KeygenPath
- if len(keygenPath) == 0 {
- keygenPath = "ssh-keygen"
- }
-
- stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", keygenPath, "-lf", tmpName)
- if err != nil {
- return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
- }
- if strings.Contains(stdout, "is not a public key file") {
- return "", 0, ErrKeyUnableVerify{stdout}
- }
-
- fields := strings.Split(stdout, " ")
- if len(fields) < 4 {
- return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
- }
-
- keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
- length, err := strconv.ParseInt(fields[0], 10, 32)
- if err != nil {
- return "", 0, err
- }
- return strings.ToLower(keyType), int(length), nil
-}
diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go
index 3650f1892f..21e4ddf62e 100644
--- a/models/asymkey/ssh_key_test.go
+++ b/models/asymkey/ssh_key_test.go
@@ -42,29 +42,7 @@ func Test_SSHParsePublicKey(t *testing.T) {
keyTypeN, lengthN, err := SSHNativeParsePublicKey(tc.content)
assert.NoError(t, err)
assert.Equal(t, tc.keyType, keyTypeN)
- assert.EqualValues(t, tc.length, lengthN)
- })
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHKeyGenParsePublicKey(tc.content)
- if err != nil {
- // Some servers do not support ecdsa format.
- if !strings.Contains(err.Error(), "line 1 too long:") {
- assert.FailNow(t, "%v", err)
- }
- }
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.EqualValues(t, tc.length, lengthK)
- })
- t.Run("SSHParseKeyNative", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content)
- if err != nil {
- assert.FailNow(t, "%v", err)
- }
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.EqualValues(t, tc.length, lengthK)
+ assert.Equal(t, tc.length, lengthN)
})
})
}
@@ -186,14 +164,6 @@ func Test_calcFingerprint(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, tc.fp, fpN)
})
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- fpK, err := calcFingerprintSSHKeygen(tc.content)
- assert.NoError(t, err)
- assert.Equal(t, tc.fp, fpK)
- })
})
}
}
diff --git a/models/asymkey/ssh_key_verify.go b/models/asymkey/ssh_key_verify.go
index 208288c77b..0cf29ca9f1 100644
--- a/models/asymkey/ssh_key_verify.go
+++ b/models/asymkey/ssh_key_verify.go
@@ -4,8 +4,8 @@
package asymkey
import (
- "bytes"
"context"
+ "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -30,12 +30,12 @@ func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signat
return "", ErrKeyNotExist{}
}
- err = sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea")
+ err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
if err != nil {
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
// see https://github.com/PowerShell/PowerShell/issues/5974
- if sshsig.Verify(bytes.NewBuffer([]byte(token+"\r\n")), []byte(signature), []byte(key.Content), "gitea") != nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
+ log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
}
diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go
index 897ff3fc9e..3eae19b2a5 100644
--- a/models/auth/access_token_scope.go
+++ b/models/auth/access_token_scope.go
@@ -5,6 +5,7 @@ package auth
import (
"fmt"
+ "slices"
"strings"
"code.gitea.io/gitea/models/perm"
@@ -14,7 +15,7 @@ import (
type AccessTokenScopeCategory int
const (
- AccessTokenScopeCategoryActivityPub = iota
+ AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota
AccessTokenScopeCategoryAdmin
AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values
AccessTokenScopeCategoryNotification
@@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
},
}
+func GetAccessTokenCategories() (res []string) {
+ for _, cat := range accessTokenScopes[Read] {
+ res = append(res, strings.TrimPrefix(string(cat), "read:"))
+ }
+ slices.Sort(res)
+ return res
+}
+
// GetRequiredScopes gets the specific scopes for a given level and categories
func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope {
scopes := make([]AccessTokenScope, 0, len(scopeCategories))
@@ -204,12 +213,7 @@ func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTok
// ContainsCategory checks if a list of categories contains a specific category
func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool {
- for _, c := range categories {
- if c == category {
- return true
- }
- }
- return false
+ return slices.Contains(categories, category)
}
// GetScopeLevelFromAccessMode converts permission access mode to scope level
@@ -270,6 +274,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
// StringSlice returns the AccessTokenScope as a []string
func (s AccessTokenScope) StringSlice() []string {
+ if s == "" {
+ return nil
+ }
return strings.Split(string(s), ",")
}
@@ -283,6 +290,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
return bitmap.toScope(), nil
}
+func (s AccessTokenScope) HasPermissionScope() bool {
+ return s != "" && s != AccessTokenScopePublicOnly
+}
+
// PublicOnly checks if this token scope is limited to public resources
func (s AccessTokenScope) PublicOnly() (bool, error) {
bitmap, err := s.parse()
diff --git a/models/auth/access_token_scope_test.go b/models/auth/access_token_scope_test.go
index a6097e45d7..b93c25528f 100644
--- a/models/auth/access_token_scope_test.go
+++ b/models/auth/access_token_scope_test.go
@@ -17,6 +17,7 @@ type scopeTestNormalize struct {
}
func TestAccessTokenScope_Normalize(t *testing.T) {
+ assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
tests := []scopeTestNormalize{
{"", "", nil},
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
@@ -25,13 +26,13 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
+ scopeTestNormalize{AccessTokenScope("read:" + scope), AccessTokenScope("read:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope("write:" + scope), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
)
}
@@ -59,23 +60,23 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
{"public-only", "read:issue", false, nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("write:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), false, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("write:" + scope), false, nil,
},
)
}
diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go
index 81f07d1a83..54ff5a0d75 100644
--- a/models/auth/auth_token.go
+++ b/models/auth/auth_token.go
@@ -15,7 +15,7 @@ import (
var ErrAuthTokenNotExist = util.NewNotExistErrorf("auth token does not exist")
-type AuthToken struct { //nolint:revive
+type AuthToken struct { //nolint:revive // export stutter
ID string `xorm:"pk"`
TokenHash string
UserID int64 `xorm:"INDEX"`
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index c270e4856e..c2b6690116 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -12,6 +12,7 @@ import (
"fmt"
"net"
"net/url"
+ "slices"
"strings"
"code.gitea.io/gitea/models/db"
@@ -511,12 +512,7 @@ func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error {
// ScopeContains returns true if the grant scope contains the specified scope
func (grant *OAuth2Grant) ScopeContains(scope string) bool {
- for _, currentScope := range strings.Split(grant.Scope, " ") {
- if scope == currentScope {
- return true
- }
- }
- return false
+ return slices.Contains(strings.Split(grant.Scope, " "), scope)
}
// SetNonce updates the current nonce value of a grant
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index 43daa0b5ec..c6626b283e 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -25,7 +25,7 @@ func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
assert.NoError(b, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_, _ = app.GenerateClientSecret(db.DefaultContext)
}
}
@@ -126,7 +126,7 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
assert.NotNil(t, grant)
assert.Equal(t, int64(2), grant.UserID)
assert.Equal(t, int64(1), grant.ApplicationID)
- assert.Equal(t, "", grant.Scope)
+ assert.Empty(t, grant.Scope)
}
//////////////////// Grant
diff --git a/models/auth/source.go b/models/auth/source.go
index a3a250cd91..7d7bc0f03c 100644
--- a/models/auth/source.go
+++ b/models/auth/source.go
@@ -58,6 +58,15 @@ var Names = map[Type]string{
// Config represents login config as far as the db is concerned
type Config interface {
convert.Conversion
+ SetAuthSource(*Source)
+}
+
+type ConfigBase struct {
+ AuthSource *Source
+}
+
+func (p *ConfigBase) SetAuthSource(s *Source) {
+ p.AuthSource = s
}
// SkipVerifiable configurations provide a IsSkipVerify to check if SkipVerify is set
@@ -104,19 +113,15 @@ func RegisterTypeConfig(typ Type, exemplar Config) {
}
}
-// SourceSettable configurations can have their authSource set on them
-type SourceSettable interface {
- SetAuthSource(*Source)
-}
-
// Source represents an external way for authorizing users.
type Source struct {
- ID int64 `xorm:"pk autoincr"`
- Type Type
- Name string `xorm:"UNIQUE"`
- IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
- IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
- Cfg convert.Conversion `xorm:"TEXT"`
+ ID int64 `xorm:"pk autoincr"`
+ Type Type
+ Name string `xorm:"UNIQUE"`
+ IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
+ Cfg Config `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -140,9 +145,7 @@ func (source *Source) BeforeSet(colName string, val xorm.Cell) {
return
}
source.Cfg = constructor()
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
}
}
@@ -200,6 +203,10 @@ func (source *Source) SkipVerify() bool {
return ok && skipVerifiable.IsSkipVerify()
}
+func (source *Source) TwoFactorShouldSkip() bool {
+ return source.TwoFactorPolicy == "skip"
+}
+
// CreateSource inserts a AuthSource in the DB if not already
// existing with the given name.
func CreateSource(ctx context.Context, source *Source) error {
@@ -223,9 +230,7 @@ func CreateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
@@ -320,9 +325,7 @@ func UpdateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 36e76d5e28..64c7460b64 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -13,10 +13,14 @@ import (
"code.gitea.io/gitea/modules/json"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type TestSource struct {
+ auth_model.ConfigBase
+
Provider string
ClientID string
ClientSecret string
@@ -54,7 +58,8 @@ func TestDumpAuthSource(t *testing.T) {
sb := new(strings.Builder)
- db.DumpTables([]*schemas.Table{authSourceSchema}, sb)
-
+ // TODO: this test is quite hacky, it should use a low-level "select" (without model processors) but not a database dump
+ engine := db.GetEngine(db.DefaultContext).(*xorm.Engine)
+ require.NoError(t, engine.DumpTables([]*schemas.Table{authSourceSchema}, sb))
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
}
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index d0c341a192..200ce7c7c0 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -164,3 +164,13 @@ func DeleteTwoFactorByID(ctx context.Context, id, userID int64) error {
}
return nil
}
+
+func HasTwoFactorOrWebAuthn(ctx context.Context, id int64) (bool, error) {
+ has, err := HasTwoFactorByUID(ctx, id)
+ if err != nil {
+ return false, err
+ } else if has {
+ return true, nil
+ }
+ return HasWebAuthnRegistrationsByUID(ctx, id)
+}
diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go
index f1cf398adf..654427e974 100644
--- a/models/auth/webauthn_test.go
+++ b/models/auth/webauthn_test.go
@@ -44,7 +44,7 @@ func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 1
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
- unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
}
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
@@ -52,7 +52,7 @@ func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 0xffffffff
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
- unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
}
func TestCreateCredential(t *testing.T) {
@@ -63,5 +63,5 @@ func TestCreateCredential(t *testing.T) {
assert.Equal(t, "WebAuthn Created Credential", res.Name)
assert.Equal(t, []byte("Test"), res.CredentialID)
- unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
}
diff --git a/models/db/collation.go b/models/db/collation.go
index a7db9f5442..79ade87380 100644
--- a/models/db/collation.go
+++ b/models/db/collation.go
@@ -140,7 +140,7 @@ func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) {
}
func CheckCollationsDefaultEngine() (*CheckCollationsResult, error) {
- return CheckCollations(x)
+ return CheckCollations(xormEngine)
}
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
diff --git a/models/db/context.go b/models/db/context.go
index 171e26b933..ad99ada8c8 100644
--- a/models/db/context.go
+++ b/models/db/context.go
@@ -67,7 +67,7 @@ func contextSafetyCheck(e Engine) {
_ = e.SQL("SELECT 1").Iterate(&m{}, func(int, any) error {
callers := make([]uintptr, 32)
callerNum := runtime.Callers(1, callers)
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if funcName := runtime.FuncForPC(callers[i]).Name(); funcName == "xorm.io/xorm.(*Session).Iterate" {
contextSafetyDeniedFuncPCs = append(contextSafetyDeniedFuncPCs, callers[i])
}
@@ -82,7 +82,7 @@ func contextSafetyCheck(e Engine) {
// it should be very fast: xxxx ns/op
callers := make([]uintptr, 32)
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
panic(errors.New("using database context in an iterator would cause corrupted results"))
}
@@ -94,7 +94,7 @@ func GetEngine(ctx context.Context) Engine {
if e := getExistingEngine(ctx); e != nil {
return e
}
- return x.Context(ctx)
+ return xormEngine.Context(ctx)
}
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
@@ -155,7 +155,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) {
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
}
- sess := x.NewSession()
+ sess := xormEngine.NewSession()
if err := sess.Begin(); err != nil {
_ = sess.Close()
return nil, nil, err
@@ -178,8 +178,17 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
return txWithNoCheck(parentCtx, f)
}
+// WithTx2 is similar to WithTx, but it has two return values: result and error.
+func WithTx2[T any](parentCtx context.Context, f func(ctx context.Context) (T, error)) (ret T, errRet error) {
+ errRet = WithTx(parentCtx, func(ctx context.Context) (errInner error) {
+ ret, errInner = f(ctx)
+ return errInner
+ })
+ return ret, errRet
+}
+
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
- sess := x.NewSession()
+ sess := xormEngine.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
@@ -289,6 +298,9 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
// Timestamps of the entities won't be updated
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
+ if len(ids) == 0 {
+ return nil
+ }
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
return err
}
@@ -322,7 +334,7 @@ func CountByBean(ctx context.Context, bean any) (int64, error) {
// TableName returns the table name according a bean object
func TableName(bean any) string {
- return x.TableName(bean)
+ return xormEngine.TableName(bean)
}
// InTransaction returns true if the engine is in a transaction otherwise return false
diff --git a/models/db/context_test.go b/models/db/context_test.go
index e8c6b74d93..a6bd11d2ae 100644
--- a/models/db/context_test.go
+++ b/models/db/context_test.go
@@ -118,7 +118,7 @@ func TestContextSafety(t *testing.T) {
})
return nil
})
- assert.EqualValues(t, testCount, actualCount)
+ assert.Equal(t, testCount, actualCount)
// deny the bad usages
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
diff --git a/models/db/convert.go b/models/db/convert.go
index 8c124471ab..80b0f7b04b 100644
--- a/models/db/convert.go
+++ b/models/db/convert.go
@@ -16,30 +16,30 @@ import (
// ConvertDatabaseTable converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
func ConvertDatabaseTable() error {
- if x.Dialect().URI().DBType != schemas.MYSQL {
+ if xormEngine.Dialect().URI().DBType != schemas.MYSQL {
return nil
}
- r, err := CheckCollations(x)
+ r, err := CheckCollations(xormEngine)
if err != nil {
return err
}
- _, err = x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
+ _, err = xormEngine.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
if err != nil {
return err
}
- tables, err := x.DBMetas()
+ tables, err := xormEngine.DBMetas()
if err != nil {
return err
}
for _, table := range tables {
- if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
+ if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
return err
}
- if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
+ if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
return err
}
}
@@ -49,11 +49,11 @@ func ConvertDatabaseTable() error {
// ConvertVarcharToNVarchar converts database and tables from varchar to nvarchar if it's mssql
func ConvertVarcharToNVarchar() error {
- if x.Dialect().URI().DBType != schemas.MSSQL {
+ if xormEngine.Dialect().URI().DBType != schemas.MSSQL {
return nil
}
- sess := x.NewSession()
+ sess := xormEngine.NewSession()
defer sess.Close()
res, err := sess.QuerySliceString(`SELECT 'ALTER TABLE ' + OBJECT_NAME(SC.object_id) + ' MODIFY SC.name NVARCHAR(' + CONVERT(VARCHAR(5),SC.max_length) + ')'
FROM SYS.columns SC
diff --git a/models/db/engine.go b/models/db/engine.go
index b17188945a..ba287d58f0 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -8,17 +8,10 @@ import (
"context"
"database/sql"
"fmt"
- "io"
"reflect"
"strings"
- "time"
-
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
- "xorm.io/xorm/contexts"
- "xorm.io/xorm/names"
"xorm.io/xorm/schemas"
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
@@ -27,9 +20,9 @@ import (
)
var (
- x *xorm.Engine
- tables []any
- initFuncs []func() error
+ xormEngine *xorm.Engine
+ registeredModels []any
+ registeredInitFuncs []func() error
)
// Engine represents a xorm engine or session.
@@ -70,167 +63,38 @@ type Engine interface {
// TableInfo returns table's information via an object
func TableInfo(v any) (*schemas.Table, error) {
- return x.TableInfo(v)
+ return xormEngine.TableInfo(v)
}
-// DumpTables dump tables information
-func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error {
- return x.DumpTables(tables, w, tp...)
-}
-
-// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync
+// RegisterModel registers model, if initFuncs provided, it will be invoked after data model sync
func RegisterModel(bean any, initFunc ...func() error) {
- tables = append(tables, bean)
- if len(initFuncs) > 0 && initFunc[0] != nil {
- initFuncs = append(initFuncs, initFunc[0])
+ registeredModels = append(registeredModels, bean)
+ if len(registeredInitFuncs) > 0 && initFunc[0] != nil {
+ registeredInitFuncs = append(registeredInitFuncs, initFunc[0])
}
}
-func init() {
- gonicNames := []string{"SSL", "UID"}
- for _, name := range gonicNames {
- names.LintGonicMapper[name] = true
- }
-}
-
-// newXORMEngine returns a new XORM engine from the configuration
-func newXORMEngine() (*xorm.Engine, error) {
- connStr, err := setting.DBConnStr()
- if err != nil {
- return nil, err
- }
-
- var engine *xorm.Engine
-
- if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
- // OK whilst we sort out our schema issues - create a schema aware postgres
- registerPostgresSchemaDriver()
- engine, err = xorm.NewEngine("postgresschema", connStr)
- } else {
- engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
- }
-
- if err != nil {
- return nil, err
- }
- if setting.Database.Type == "mysql" {
- engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
- } else if setting.Database.Type == "mssql" {
- engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
- }
- engine.SetSchema(setting.Database.Schema)
- return engine, nil
-}
-
// SyncAllTables sync the schemas of all tables, is required by unit test code
func SyncAllTables() error {
- _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
+ _, err := xormEngine.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
WarnIfDatabaseColumnMissed: true,
- }, tables...)
+ }, registeredModels...)
return err
}
-// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
-func InitEngine(ctx context.Context) error {
- xormEngine, err := newXORMEngine()
- if err != nil {
- if strings.Contains(err.Error(), "SQLite3 support") {
- return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
- }
- return fmt.Errorf("failed to connect to database: %w", err)
- }
-
- xormEngine.SetMapper(names.GonicMapper{})
- // WARNING: for serv command, MUST remove the output to os.stdout,
- // so use log file to instead print to stdout.
- xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL))
- xormEngine.ShowSQL(setting.Database.LogSQL)
- xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns)
- xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns)
- xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
- xormEngine.SetDefaultContext(ctx)
-
- if setting.Database.SlowQueryThreshold > 0 {
- xormEngine.AddHook(&SlowQueryHook{
- Threshold: setting.Database.SlowQueryThreshold,
- Logger: log.GetLogger("xorm"),
- })
- }
-
- SetDefaultEngine(ctx, xormEngine)
- return nil
-}
-
-// SetDefaultEngine sets the default engine for db
-func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
- x = eng
- DefaultContext = &Context{Context: ctx, engine: x}
-}
-
-// UnsetDefaultEngine closes and unsets the default engine
-// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
-// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close
-// Global database engine related functions are all racy and there is no graceful close right now.
-func UnsetDefaultEngine() {
- if x != nil {
- _ = x.Close()
- x = nil
- }
- DefaultContext = nil
-}
-
-// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
-// This function must never call .Sync() if the provided migration function fails.
-// When called from the "doctor" command, the migration function is a version check
-// that prevents the doctor from fixing anything in the database if the migration level
-// is different from the expected value.
-func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
- if err = InitEngine(ctx); err != nil {
- return err
- }
-
- if err = x.Ping(); err != nil {
- return err
- }
-
- preprocessDatabaseCollation(x)
-
- // We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
- // If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
- //
- // Installation should only be being re-run if users want to recover an old database.
- // However, we should think carefully about should we support re-install on an installed instance,
- // as there may be other problems due to secret reinitialization.
- if err = migrateFunc(x); err != nil {
- return fmt.Errorf("migrate: %w", err)
- }
-
- if err = SyncAllTables(); err != nil {
- return fmt.Errorf("sync database struct error: %w", err)
- }
-
- for _, initFunc := range initFuncs {
- if err := initFunc(); err != nil {
- return fmt.Errorf("initFunc failed: %w", err)
- }
- }
-
- return nil
-}
-
// NamesToBean return a list of beans or an error
func NamesToBean(names ...string) ([]any, error) {
beans := []any{}
if len(names) == 0 {
- beans = append(beans, tables...)
+ beans = append(beans, registeredModels...)
return beans, nil
}
// Need to map provided names to beans...
beanMap := make(map[string]any)
- for _, bean := range tables {
+ for _, bean := range registeredModels {
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
- beanMap[strings.ToLower(x.TableName(bean))] = bean
- beanMap[strings.ToLower(x.TableName(bean, true))] = bean
+ beanMap[strings.ToLower(xormEngine.TableName(bean))] = bean
+ beanMap[strings.ToLower(xormEngine.TableName(bean, true))] = bean
}
gotBean := make(map[any]bool)
@@ -247,36 +111,9 @@ func NamesToBean(names ...string) ([]any, error) {
return beans, nil
}
-// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
-func DumpDatabase(filePath, dbType string) error {
- var tbs []*schemas.Table
- for _, t := range tables {
- t, err := x.TableInfo(t)
- if err != nil {
- return err
- }
- tbs = append(tbs, t)
- }
-
- type Version struct {
- ID int64 `xorm:"pk autoincr"`
- Version int64
- }
- t, err := x.TableInfo(&Version{})
- if err != nil {
- return err
- }
- tbs = append(tbs, t)
-
- if len(dbType) > 0 {
- return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
- }
- return x.DumpTablesToFile(tbs, filePath)
-}
-
// MaxBatchInsertSize returns the table's max batch insert size
func MaxBatchInsertSize(bean any) int {
- t, err := x.TableInfo(bean)
+ t, err := xormEngine.TableInfo(bean)
if err != nil {
return 50
}
@@ -285,18 +122,18 @@ func MaxBatchInsertSize(bean any) int {
// IsTableNotEmpty returns true if table has at least one record
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
- return x.Table(beanOrTableName).Exist()
+ return xormEngine.Table(beanOrTableName).Exist()
}
// DeleteAllRecords will delete all the records of this table
func DeleteAllRecords(tableName string) error {
- _, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
+ _, err := xormEngine.Exec("DELETE FROM " + tableName)
return err
}
// GetMaxID will return max id of the table
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
- _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
+ _, err = xormEngine.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
return maxID, err
}
@@ -308,24 +145,3 @@ func SetLogSQL(ctx context.Context, on bool) {
sess.Engine().ShowSQL(on)
}
}
-
-type SlowQueryHook struct {
- Threshold time.Duration
- Logger log.Logger
-}
-
-var _ contexts.Hook = &SlowQueryHook{}
-
-func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
- return c.Ctx, nil
-}
-
-func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
- if c.ExecuteTime >= h.Threshold {
- // 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
- // is being displayed (the function that ultimately wants to execute the query in the code)
- // instead of the function of the slow query hook being called.
- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
- }
- return nil
-}
diff --git a/models/db/engine_dump.go b/models/db/engine_dump.go
new file mode 100644
index 0000000000..63f2d4e093
--- /dev/null
+++ b/models/db/engine_dump.go
@@ -0,0 +1,33 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package db
+
+import "xorm.io/xorm/schemas"
+
+// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
+func DumpDatabase(filePath, dbType string) error {
+ var tbs []*schemas.Table
+ for _, t := range registeredModels {
+ t, err := xormEngine.TableInfo(t)
+ if err != nil {
+ return err
+ }
+ tbs = append(tbs, t)
+ }
+
+ type Version struct {
+ ID int64 `xorm:"pk autoincr"`
+ Version int64
+ }
+ t, err := xormEngine.TableInfo(&Version{})
+ if err != nil {
+ return err
+ }
+ tbs = append(tbs, t)
+
+ if dbType != "" {
+ return xormEngine.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
+ }
+ return xormEngine.DumpTablesToFile(tbs, filePath)
+}
diff --git a/models/db/engine_hook.go b/models/db/engine_hook.go
new file mode 100644
index 0000000000..8709a2c2a1
--- /dev/null
+++ b/models/db/engine_hook.go
@@ -0,0 +1,47 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package db
+
+import (
+ "context"
+ "time"
+
+ "code.gitea.io/gitea/modules/gtprof"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "xorm.io/xorm/contexts"
+)
+
+type EngineHook struct {
+ Threshold time.Duration
+ Logger log.Logger
+}
+
+var _ contexts.Hook = (*EngineHook)(nil)
+
+func (*EngineHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
+ ctx, _ := gtprof.GetTracer().Start(c.Ctx, gtprof.TraceSpanDatabase)
+ return ctx, nil
+}
+
+func (h *EngineHook) AfterProcess(c *contexts.ContextHook) error {
+ span := gtprof.GetContextSpan(c.Ctx)
+ if span != nil {
+ // Do not record SQL parameters here:
+ // * It shouldn't expose the parameters because they contain sensitive information, end users need to report the trace details safely.
+ // * Some parameters contain quite long texts, waste memory and are difficult to display.
+ span.SetAttributeString(gtprof.TraceAttrDbSQL, c.SQL)
+ span.End()
+ } else {
+ setting.PanicInDevOrTesting("span in database engine hook is nil")
+ }
+ if c.ExecuteTime >= h.Threshold {
+ // 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
+ // is being displayed (the function that ultimately wants to execute the query in the code)
+ // instead of the function of the slow query hook being called.
+ h.Logger.Log(8, &log.Event{Level: log.WARN}, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
+ }
+ return nil
+}
diff --git a/models/db/engine_init.go b/models/db/engine_init.go
new file mode 100644
index 0000000000..bb02aff274
--- /dev/null
+++ b/models/db/engine_init.go
@@ -0,0 +1,141 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package db
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "xorm.io/xorm"
+ "xorm.io/xorm/names"
+)
+
+func init() {
+ gonicNames := []string{"SSL", "UID"}
+ for _, name := range gonicNames {
+ names.LintGonicMapper[name] = true
+ }
+}
+
+// newXORMEngine returns a new XORM engine from the configuration
+func newXORMEngine() (*xorm.Engine, error) {
+ connStr, err := setting.DBConnStr()
+ if err != nil {
+ return nil, err
+ }
+
+ var engine *xorm.Engine
+
+ if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
+ // OK whilst we sort out our schema issues - create a schema aware postgres
+ registerPostgresSchemaDriver()
+ engine, err = xorm.NewEngine("postgresschema", connStr)
+ } else {
+ engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ switch setting.Database.Type {
+ case "mysql":
+ engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
+ case "mssql":
+ engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
+ }
+ engine.SetSchema(setting.Database.Schema)
+ return engine, nil
+}
+
+// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
+func InitEngine(ctx context.Context) error {
+ xe, err := newXORMEngine()
+ if err != nil {
+ if strings.Contains(err.Error(), "SQLite3 support") {
+ return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
+ }
+ return fmt.Errorf("failed to connect to database: %w", err)
+ }
+
+ xe.SetMapper(names.GonicMapper{})
+ // WARNING: for serv command, MUST remove the output to os.stdout,
+ // so use log file to instead print to stdout.
+ xe.SetLogger(NewXORMLogger(setting.Database.LogSQL))
+ xe.ShowSQL(setting.Database.LogSQL)
+ xe.SetMaxOpenConns(setting.Database.MaxOpenConns)
+ xe.SetMaxIdleConns(setting.Database.MaxIdleConns)
+ xe.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
+ xe.SetDefaultContext(ctx)
+
+ if setting.Database.SlowQueryThreshold > 0 {
+ xe.AddHook(&EngineHook{
+ Threshold: setting.Database.SlowQueryThreshold,
+ Logger: log.GetLogger("xorm"),
+ })
+ }
+
+ SetDefaultEngine(ctx, xe)
+ return nil
+}
+
+// SetDefaultEngine sets the default engine for db
+func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
+ xormEngine = eng
+ DefaultContext = &Context{Context: ctx, engine: xormEngine}
+}
+
+// UnsetDefaultEngine closes and unsets the default engine
+// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
+// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `xormEngine` and DefaultContext without close
+// Global database engine related functions are all racy and there is no graceful close right now.
+func UnsetDefaultEngine() {
+ if xormEngine != nil {
+ _ = xormEngine.Close()
+ xormEngine = nil
+ }
+ DefaultContext = nil
+}
+
+// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
+// This function must never call .Sync() if the provided migration function fails.
+// When called from the "doctor" command, the migration function is a version check
+// that prevents the doctor from fixing anything in the database if the migration level
+// is different from the expected value.
+func InitEngineWithMigration(ctx context.Context, migrateFunc func(context.Context, *xorm.Engine) error) (err error) {
+ if err = InitEngine(ctx); err != nil {
+ return err
+ }
+
+ if err = xormEngine.Ping(); err != nil {
+ return err
+ }
+
+ preprocessDatabaseCollation(xormEngine)
+
+ // We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
+ // If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
+ //
+ // Installation should only be being re-run if users want to recover an old database.
+ // However, we should think carefully about should we support re-install on an installed instance,
+ // as there may be other problems due to secret reinitialization.
+ if err = migrateFunc(ctx, xormEngine); err != nil {
+ return fmt.Errorf("migrate: %w", err)
+ }
+
+ if err = SyncAllTables(); err != nil {
+ return fmt.Errorf("sync database struct error: %w", err)
+ }
+
+ for _, initFunc := range registeredInitFuncs {
+ if err := initFunc(); err != nil {
+ return fmt.Errorf("initFunc failed: %w", err)
+ }
+ }
+
+ return nil
+}
diff --git a/models/db/engine_test.go b/models/db/engine_test.go
index e3dbfbdc24..a236f83735 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -15,6 +15,7 @@ import (
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestDumpDatabase(t *testing.T) {
@@ -51,7 +52,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.EqualValues(t, countBefore, countAfter)
+ assert.Equal(t, countBefore, countAfter)
}
func TestPrimaryKeys(t *testing.T) {
@@ -62,9 +63,7 @@ func TestPrimaryKeys(t *testing.T) {
// Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called.
beans, err := db.NamesToBean()
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
whitelist := map[string]string{
"the_table_name_to_skip_checking": "Write a note here to explain why",
@@ -79,8 +78,6 @@ func TestPrimaryKeys(t *testing.T) {
t.Logf("ignore %q because %q", table.Name, why)
continue
}
- if len(table.PrimaryKeys) == 0 {
- t.Errorf("table %q has no primary key", table.Name)
- }
+ assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
}
}
diff --git a/models/db/error.go b/models/db/error.go
index 665e970e17..d47c7adac4 100644
--- a/models/db/error.go
+++ b/models/db/error.go
@@ -65,7 +65,7 @@ func (err ErrNotExist) Error() string {
if err.ID != 0 {
return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
}
- return fmt.Sprintf("%s does not exist", name)
+ return name + " does not exist"
}
// Unwrap unwraps this as a ErrNotExist err
diff --git a/models/db/list_test.go b/models/db/list_test.go
index 45194611f8..170473a968 100644
--- a/models/db/list_test.go
+++ b/models/db/list_test.go
@@ -47,6 +47,6 @@ func TestFind(t *testing.T) {
repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts)
assert.NoError(t, err)
- assert.EqualValues(t, cnt, newCnt)
+ assert.Equal(t, cnt, newCnt)
assert.Len(t, repoUnits, repoUnitCount)
}
diff --git a/models/db/log.go b/models/db/log.go
index 307788ea2e..a9df6f541d 100644
--- a/models/db/log.go
+++ b/models/db/log.go
@@ -29,7 +29,7 @@ const stackLevel = 8
// Log a message with defined skip and at logging level
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...any) {
- l.logger.Log(skip+1, level, format, v...)
+ l.logger.Log(skip+1, &log.Event{Level: level}, format, v...)
}
// Debug show debug log
diff --git a/models/db/name.go b/models/db/name.go
index 51be33a8bc..48c7fdbce5 100644
--- a/models/db/name.go
+++ b/models/db/name.go
@@ -5,21 +5,13 @@ package db
import (
"fmt"
- "regexp"
+ "slices"
"strings"
"unicode/utf8"
"code.gitea.io/gitea/modules/util"
)
-var (
- // ErrNameEmpty name is empty error
- ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
-
- // AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
- AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
-)
-
// ErrNameReserved represents a "reserved name" error.
type ErrNameReserved struct {
Name string
@@ -82,20 +74,18 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
// IsUsableName checks if name is reserved or pattern of name is not allowed
// based on given reserved names and patterns.
-// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
-func IsUsableName(names, patterns []string, name string) error {
+// Names are exact match, patterns can be a prefix or suffix match with placeholder '*'.
+func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
- return ErrNameEmpty
+ return util.NewInvalidArgumentErrorf("name is empty")
}
- for i := range names {
- if name == names[i] {
- return ErrNameReserved{name}
- }
+ if slices.Contains(reservedNames, name) {
+ return ErrNameReserved{name}
}
- for _, pat := range patterns {
+ for _, pat := range reservedPatterns {
if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) ||
(pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) {
return ErrNamePatternNotAllowed{pat}
diff --git a/models/db/search.go b/models/db/search.go
index e0a1b6bde9..44d54f21fc 100644
--- a/models/db/search.go
+++ b/models/db/search.go
@@ -29,7 +29,3 @@ const (
// NoConditionID means a condition to filter the records which don't match any id.
// eg: "milestone_id=-1" means "find the items without any milestone.
const NoConditionID int64 = -1
-
-// NonExistingID means a condition to match no result (eg: a non-existing user)
-// It doesn't use -1 or -2 because they are used as builtin users.
-const NonExistingID int64 = -1000000
diff --git a/models/db/sequence.go b/models/db/sequence.go
index f49ad935de..9adc5113ac 100644
--- a/models/db/sequence.go
+++ b/models/db/sequence.go
@@ -17,11 +17,11 @@ func CountBadSequences(_ context.Context) (int64, error) {
return 0, nil
}
- sess := x.NewSession()
+ sess := xormEngine.NewSession()
defer sess.Close()
var sequences []string
- schema := x.Dialect().URI().Schema
+ schema := xormEngine.Dialect().URI().Schema
sess.Engine().SetSchema("")
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?", setting.Database.Name).Find(&sequences); err != nil {
@@ -38,7 +38,7 @@ func FixBadSequences(_ context.Context) error {
return nil
}
- sess := x.NewSession()
+ sess := xormEngine.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
diff --git a/models/db/sql_postgres_with_schema.go b/models/db/sql_postgres_with_schema.go
index 64b61b2ef3..812fe4a6a6 100644
--- a/models/db/sql_postgres_with_schema.go
+++ b/models/db/sql_postgres_with_schema.go
@@ -39,7 +39,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
// and in any case pq does not implement it
- if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck
+ if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck // see above
_, err := execer.Exec(`SELECT set_config(
'search_path',
$1 || ',' || current_setting('search_path'),
@@ -64,7 +64,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// driver.String.ConvertValue will never return err for string
// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
- _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck
+ _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck // see above
if err != nil {
_ = conn.Close()
return nil, err
diff --git a/models/dbfs/dbfile.go b/models/dbfs/dbfile.go
index dd27b5c36b..eaf506fbe6 100644
--- a/models/dbfs/dbfile.go
+++ b/models/dbfs/dbfile.go
@@ -46,10 +46,7 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
blobPos := int(offset % f.blockSize)
blobOffset := offset - int64(blobPos)
blobRemaining := int(f.blockSize) - blobPos
- needRead := len(p)
- if needRead > blobRemaining {
- needRead = blobRemaining
- }
+ needRead := min(len(p), blobRemaining)
if blobOffset+int64(blobPos)+int64(needRead) > fileMeta.FileSize {
needRead = int(fileMeta.FileSize - blobOffset - int64(blobPos))
}
@@ -66,14 +63,8 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
blobData = nil
}
- canCopy := len(blobData) - blobPos
- if canCopy <= 0 {
- canCopy = 0
- }
- realRead := needRead
- if realRead > canCopy {
- realRead = canCopy
- }
+ canCopy := max(len(blobData)-blobPos, 0)
+ realRead := min(needRead, canCopy)
if realRead > 0 {
copy(p[:realRead], fileData.BlobData[blobPos:blobPos+realRead])
}
@@ -113,10 +104,7 @@ func (f *file) Write(p []byte) (n int, err error) {
blobPos := int(f.offset % f.blockSize)
blobOffset := f.offset - int64(blobPos)
blobRemaining := int(f.blockSize) - blobPos
- needWrite := len(p)
- if needWrite > blobRemaining {
- needWrite = blobRemaining
- }
+ needWrite := min(len(p), blobRemaining)
buf := make([]byte, f.blockSize)
readBytes, err := f.readAt(fileMeta, blobOffset, buf)
if err != nil && !errors.Is(err, io.EOF) {
diff --git a/models/dbfs/dbfs_test.go b/models/dbfs/dbfs_test.go
index 96cb1014c7..0257d2bd15 100644
--- a/models/dbfs/dbfs_test.go
+++ b/models/dbfs/dbfs_test.go
@@ -31,15 +31,15 @@ func TestDbfsBasic(t *testing.T) {
n, err := f.Write([]byte("0123456789")) // blocks: 0123 4567 89
assert.NoError(t, err)
- assert.EqualValues(t, 10, n)
+ assert.Equal(t, 10, n)
_, err = f.Seek(0, io.SeekStart)
assert.NoError(t, err)
buf, err := io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, 10, n)
- assert.EqualValues(t, "0123456789", string(buf))
+ assert.Equal(t, 10, n)
+ assert.Equal(t, "0123456789", string(buf))
// write some new data
_, err = f.Seek(1, io.SeekStart)
@@ -50,14 +50,14 @@ func TestDbfsBasic(t *testing.T) {
// read from offset
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "9", string(buf))
+ assert.Equal(t, "9", string(buf))
// read all
_, err = f.Seek(0, io.SeekStart)
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghi9", string(buf))
+ assert.Equal(t, "0bcdefghi9", string(buf))
// write to new size
_, err = f.Seek(-1, io.SeekEnd)
@@ -68,7 +68,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP", string(buf))
// write beyond EOF and fill with zero
_, err = f.Seek(5, io.SeekCurrent)
@@ -79,7 +79,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf))
// write to the block with zeros
_, err = f.Seek(-6, io.SeekCurrent)
@@ -90,7 +90,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf))
assert.NoError(t, f.Close())
@@ -117,7 +117,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
stat, err := f.Stat()
assert.NoError(t, err)
- assert.EqualValues(t, "test.txt", stat.Name())
+ assert.Equal(t, "test.txt", stat.Name())
assert.EqualValues(t, 0, stat.Size())
_, err = f.Write([]byte("0123456789"))
assert.NoError(t, err)
@@ -144,7 +144,7 @@ func TestDbfsReadWrite(t *testing.T) {
line, err := f2r.ReadString('\n')
assert.NoError(t, err)
- assert.EqualValues(t, "line 1\n", line)
+ assert.Equal(t, "line 1\n", line)
_, err = f2r.ReadString('\n')
assert.ErrorIs(t, err, io.EOF)
@@ -153,7 +153,7 @@ func TestDbfsReadWrite(t *testing.T) {
line, err = f2r.ReadString('\n')
assert.NoError(t, err)
- assert.EqualValues(t, "line 2\n", line)
+ assert.Equal(t, "line 2\n", line)
_, err = f2r.ReadString('\n')
assert.ErrorIs(t, err, io.EOF)
}
@@ -186,5 +186,5 @@ func TestDbfsSeekWrite(t *testing.T) {
buf, err := io.ReadAll(fr)
assert.NoError(t, err)
- assert.EqualValues(t, "111333", string(buf))
+ assert.Equal(t, "111333", string(buf))
}
diff --git a/models/error.go b/models/error.go
deleted file mode 100644
index 75c53245de..0000000000
--- a/models/error.go
+++ /dev/null
@@ -1,552 +0,0 @@
-// Copyright 2015 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package models
-
-import (
- "fmt"
-
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/util"
-)
-
-// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
-type ErrUserOwnRepos struct {
- UID int64
-}
-
-// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
-func IsErrUserOwnRepos(err error) bool {
- _, ok := err.(ErrUserOwnRepos)
- return ok
-}
-
-func (err ErrUserOwnRepos) Error() string {
- return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
-}
-
-// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
-type ErrUserHasOrgs struct {
- UID int64
-}
-
-// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
-func IsErrUserHasOrgs(err error) bool {
- _, ok := err.(ErrUserHasOrgs)
- return ok
-}
-
-func (err ErrUserHasOrgs) Error() string {
- return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
-}
-
-// ErrUserOwnPackages notifies that the user (still) owns the packages.
-type ErrUserOwnPackages struct {
- UID int64
-}
-
-// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
-func IsErrUserOwnPackages(err error) bool {
- _, ok := err.(ErrUserOwnPackages)
- return ok
-}
-
-func (err ErrUserOwnPackages) Error() string {
- return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
-}
-
-// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
-type ErrDeleteLastAdminUser struct {
- UID int64
-}
-
-// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
-func IsErrDeleteLastAdminUser(err error) bool {
- _, ok := err.(ErrDeleteLastAdminUser)
- return ok
-}
-
-func (err ErrDeleteLastAdminUser) Error() string {
- return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
-}
-
-// ErrNoPendingRepoTransfer is an error type for repositories without a pending
-// transfer request
-type ErrNoPendingRepoTransfer struct {
- RepoID int64
-}
-
-func (err ErrNoPendingRepoTransfer) Error() string {
- return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
-}
-
-// IsErrNoPendingTransfer is an error type when a repository has no pending
-// transfers
-func IsErrNoPendingTransfer(err error) bool {
- _, ok := err.(ErrNoPendingRepoTransfer)
- return ok
-}
-
-func (err ErrNoPendingRepoTransfer) Unwrap() error {
- return util.ErrNotExist
-}
-
-// ErrRepoTransferInProgress represents the state of a repository that has an
-// ongoing transfer
-type ErrRepoTransferInProgress struct {
- Uname string
- Name string
-}
-
-// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress.
-func IsErrRepoTransferInProgress(err error) bool {
- _, ok := err.(ErrRepoTransferInProgress)
- return ok
-}
-
-func (err ErrRepoTransferInProgress) Error() string {
- return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
-}
-
-func (err ErrRepoTransferInProgress) Unwrap() error {
- return util.ErrAlreadyExist
-}
-
-// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
-type ErrInvalidCloneAddr struct {
- Host string
- IsURLError bool
- IsInvalidPath bool
- IsProtocolInvalid bool
- IsPermissionDenied bool
- LocalPath bool
-}
-
-// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
-func IsErrInvalidCloneAddr(err error) bool {
- _, ok := err.(*ErrInvalidCloneAddr)
- return ok
-}
-
-func (err *ErrInvalidCloneAddr) Error() string {
- if err.IsInvalidPath {
- return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
- }
- if err.IsProtocolInvalid {
- return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host)
- }
- if err.IsPermissionDenied {
- return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host)
- }
- if err.IsURLError {
- return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host)
- }
-
- return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
-}
-
-func (err *ErrInvalidCloneAddr) Unwrap() error {
- return util.ErrInvalidArgument
-}
-
-// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
-type ErrUpdateTaskNotExist struct {
- UUID string
-}
-
-// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist.
-func IsErrUpdateTaskNotExist(err error) bool {
- _, ok := err.(ErrUpdateTaskNotExist)
- return ok
-}
-
-func (err ErrUpdateTaskNotExist) Error() string {
- return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
-}
-
-func (err ErrUpdateTaskNotExist) Unwrap() error {
- return util.ErrNotExist
-}
-
-// ErrInvalidTagName represents a "InvalidTagName" kind of error.
-type ErrInvalidTagName struct {
- TagName string
-}
-
-// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
-func IsErrInvalidTagName(err error) bool {
- _, ok := err.(ErrInvalidTagName)
- return ok
-}
-
-func (err ErrInvalidTagName) Error() string {
- return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
-}
-
-func (err ErrInvalidTagName) Unwrap() error {
- return util.ErrInvalidArgument
-}
-
-// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
-type ErrProtectedTagName struct {
- TagName string
-}
-
-// IsErrProtectedTagName checks if an error is a ErrProtectedTagName.
-func IsErrProtectedTagName(err error) bool {
- _, ok := err.(ErrProtectedTagName)
- return ok
-}
-
-func (err ErrProtectedTagName) Error() string {
- return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
-}
-
-func (err ErrProtectedTagName) Unwrap() error {
- return util.ErrPermissionDenied
-}
-
-// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
-type ErrRepoFileAlreadyExists struct {
- Path string
-}
-
-// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists.
-func IsErrRepoFileAlreadyExists(err error) bool {
- _, ok := err.(ErrRepoFileAlreadyExists)
- return ok
-}
-
-func (err ErrRepoFileAlreadyExists) Error() string {
- return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
-}
-
-func (err ErrRepoFileAlreadyExists) Unwrap() error {
- return util.ErrAlreadyExist
-}
-
-// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
-type ErrRepoFileDoesNotExist struct {
- Path string
- Name string
-}
-
-// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist.
-func IsErrRepoFileDoesNotExist(err error) bool {
- _, ok := err.(ErrRepoFileDoesNotExist)
- return ok
-}
-
-func (err ErrRepoFileDoesNotExist) Error() string {
- return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
-}
-
-func (err ErrRepoFileDoesNotExist) Unwrap() error {
- return util.ErrNotExist
-}
-
-// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
-type ErrFilenameInvalid struct {
- Path string
-}
-
-// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid.
-func IsErrFilenameInvalid(err error) bool {
- _, ok := err.(ErrFilenameInvalid)
- return ok
-}
-
-func (err ErrFilenameInvalid) Error() string {
- return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
-}
-
-func (err ErrFilenameInvalid) Unwrap() error {
- return util.ErrInvalidArgument
-}
-
-// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
-type ErrUserCannotCommit struct {
- UserName string
-}
-
-// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit.
-func IsErrUserCannotCommit(err error) bool {
- _, ok := err.(ErrUserCannotCommit)
- return ok
-}
-
-func (err ErrUserCannotCommit) Error() string {
- return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
-}
-
-func (err ErrUserCannotCommit) Unwrap() error {
- return util.ErrPermissionDenied
-}
-
-// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
-type ErrFilePathInvalid struct {
- Message string
- Path string
- Name string
- Type git.EntryMode
-}
-
-// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid.
-func IsErrFilePathInvalid(err error) bool {
- _, ok := err.(ErrFilePathInvalid)
- return ok
-}
-
-func (err ErrFilePathInvalid) Error() string {
- if err.Message != "" {
- return err.Message
- }
- return fmt.Sprintf("path is invalid [path: %s]", err.Path)
-}
-
-func (err ErrFilePathInvalid) Unwrap() error {
- return util.ErrInvalidArgument
-}
-
-// ErrFilePathProtected represents a "FilePathProtected" kind of error.
-type ErrFilePathProtected struct {
- Message string
- Path string
-}
-
-// IsErrFilePathProtected checks if an error is an ErrFilePathProtected.
-func IsErrFilePathProtected(err error) bool {
- _, ok := err.(ErrFilePathProtected)
- return ok
-}
-
-func (err ErrFilePathProtected) Error() string {
- if err.Message != "" {
- return err.Message
- }
- return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
-}
-
-func (err ErrFilePathProtected) Unwrap() error {
- return util.ErrPermissionDenied
-}
-
-// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
-type ErrDisallowedToMerge struct {
- Reason string
-}
-
-// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
-func IsErrDisallowedToMerge(err error) bool {
- _, ok := err.(ErrDisallowedToMerge)
- return ok
-}
-
-func (err ErrDisallowedToMerge) Error() string {
- return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
-}
-
-func (err ErrDisallowedToMerge) Unwrap() error {
- return util.ErrPermissionDenied
-}
-
-// ErrTagAlreadyExists represents an error that tag with such name already exists.
-type ErrTagAlreadyExists struct {
- TagName string
-}
-
-// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists.
-func IsErrTagAlreadyExists(err error) bool {
- _, ok := err.(ErrTagAlreadyExists)
- return ok
-}
-
-func (err ErrTagAlreadyExists) Error() string {
- return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
-}
-
-func (err ErrTagAlreadyExists) Unwrap() error {
- return util.ErrAlreadyExist
-}
-
-// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
-type ErrSHADoesNotMatch struct {
- Path string
- GivenSHA string
- CurrentSHA string
-}
-
-// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch.
-func IsErrSHADoesNotMatch(err error) bool {
- _, ok := err.(ErrSHADoesNotMatch)
- return ok
-}
-
-func (err ErrSHADoesNotMatch) Error() string {
- return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA)
-}
-
-// ErrSHANotFound represents a "SHADoesNotMatch" kind of error.
-type ErrSHANotFound struct {
- SHA string
-}
-
-// IsErrSHANotFound checks if an error is a ErrSHANotFound.
-func IsErrSHANotFound(err error) bool {
- _, ok := err.(ErrSHANotFound)
- return ok
-}
-
-func (err ErrSHANotFound) Error() string {
- return fmt.Sprintf("sha not found [%s]", err.SHA)
-}
-
-func (err ErrSHANotFound) Unwrap() error {
- return util.ErrNotExist
-}
-
-// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
-type ErrCommitIDDoesNotMatch struct {
- GivenCommitID string
- CurrentCommitID string
-}
-
-// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch.
-func IsErrCommitIDDoesNotMatch(err error) bool {
- _, ok := err.(ErrCommitIDDoesNotMatch)
- return ok
-}
-
-func (err ErrCommitIDDoesNotMatch) Error() string {
- return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
-}
-
-// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error.
-type ErrSHAOrCommitIDNotProvided struct{}
-
-// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided.
-func IsErrSHAOrCommitIDNotProvided(err error) bool {
- _, ok := err.(ErrSHAOrCommitIDNotProvided)
- return ok
-}
-
-func (err ErrSHAOrCommitIDNotProvided) Error() string {
- return "a SHA or commit ID must be proved when updating a file"
-}
-
-// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
-type ErrInvalidMergeStyle struct {
- ID int64
- Style repo_model.MergeStyle
-}
-
-// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
-func IsErrInvalidMergeStyle(err error) bool {
- _, ok := err.(ErrInvalidMergeStyle)
- return ok
-}
-
-func (err ErrInvalidMergeStyle) Error() string {
- return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
- err.ID, err.Style)
-}
-
-func (err ErrInvalidMergeStyle) Unwrap() error {
- return util.ErrInvalidArgument
-}
-
-// ErrMergeConflicts represents an error if merging fails with a conflict
-type ErrMergeConflicts struct {
- Style repo_model.MergeStyle
- StdOut string
- StdErr string
- Err error
-}
-
-// IsErrMergeConflicts checks if an error is a ErrMergeConflicts.
-func IsErrMergeConflicts(err error) bool {
- _, ok := err.(ErrMergeConflicts)
- return ok
-}
-
-func (err ErrMergeConflicts) Error() string {
- return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
-}
-
-// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories
-type ErrMergeUnrelatedHistories struct {
- Style repo_model.MergeStyle
- StdOut string
- StdErr string
- Err error
-}
-
-// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories.
-func IsErrMergeUnrelatedHistories(err error) bool {
- _, ok := err.(ErrMergeUnrelatedHistories)
- return ok
-}
-
-func (err ErrMergeUnrelatedHistories) Error() string {
- return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
-}
-
-// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
-type ErrMergeDivergingFastForwardOnly struct {
- StdOut string
- StdErr string
- Err error
-}
-
-// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
-func IsErrMergeDivergingFastForwardOnly(err error) bool {
- _, ok := err.(ErrMergeDivergingFastForwardOnly)
- return ok
-}
-
-func (err ErrMergeDivergingFastForwardOnly) Error() string {
- return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
-}
-
-// ErrRebaseConflicts represents an error if rebase fails with a conflict
-type ErrRebaseConflicts struct {
- Style repo_model.MergeStyle
- CommitSHA string
- StdOut string
- StdErr string
- Err error
-}
-
-// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts.
-func IsErrRebaseConflicts(err error) bool {
- _, ok := err.(ErrRebaseConflicts)
- return ok
-}
-
-func (err ErrRebaseConflicts) Error() string {
- return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut)
-}
-
-// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error
-type ErrPullRequestHasMerged struct {
- ID int64
- IssueID int64
- HeadRepoID int64
- BaseRepoID int64
- HeadBranch string
- BaseBranch string
-}
-
-// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged.
-func IsErrPullRequestHasMerged(err error) bool {
- _, ok := err.(ErrPullRequestHasMerged)
- return ok
-}
-
-// Error does pretty-printing :D
-func (err ErrPullRequestHasMerged) Error() string {
- return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
- err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
-}
diff --git a/models/fixtures/access.yml b/models/fixtures/access.yml
index 4171e31fef..596046e950 100644
--- a/models/fixtures/access.yml
+++ b/models/fixtures/access.yml
@@ -171,3 +171,9 @@
user_id: 40
repo_id: 61
mode: 4
+
+-
+ id: 30
+ user_id: 40
+ repo_id: 1
+ mode: 2
diff --git a/models/fixtures/action_artifact.yml b/models/fixtures/action_artifact.yml
index 2c51c11ebd..ee8ef0d5ce 100644
--- a/models/fixtures/action_artifact.yml
+++ b/models/fixtures/action_artifact.yml
@@ -11,6 +11,24 @@
content_encoding: ""
artifact_path: "abc.txt"
artifact_name: "artifact-download"
+ status: 2
+ created_unix: 1712338649
+ updated_unix: 1712338649
+ expired_unix: 1720114649
+
+-
+ id: 2
+ run_id: 791
+ runner_id: 1
+ repo_id: 4
+ owner_id: 1
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: ""
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "30/20/1712348022422036662.chunk"
+ artifact_path: "abc.txt"
+ artifact_name: "artifact-download-incomplete"
status: 1
created_unix: 1712338649
updated_unix: 1712338649
@@ -69,3 +87,57 @@
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775
+
+-
+ id: 23
+ run_id: 793
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-v4-download.zip"
+ artifact_name: "artifact-v4-download"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
+
+-
+ id: 24
+ run_id: 795
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-795-1.zip"
+ artifact_name: "artifact-795-1"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
+
+-
+ id: 25
+ run_id: 795
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-795-2.zip"
+ artifact_name: "artifact-795-2"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml
index 1db849352f..09dfa6cccb 100644
--- a/models/fixtures/action_run.yml
+++ b/models/fixtures/action_run.yml
@@ -9,6 +9,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -28,6 +29,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -47,8 +49,9 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
- status: 1
+ status: 6 # running
started: 1683636528
stopped: 1683636626
created: 1683636108
@@ -66,6 +69,7 @@
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -74,3 +78,64 @@
updated: 1683636626
need_approval: 0
approved_by: 0
+-
+ id: 802
+ title: "workflow run list"
+ repo_id: 5
+ owner_id: 3
+ workflow_id: "test.yaml"
+ index: 191
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
+-
+ id: 803
+ title: "workflow run list for user"
+ repo_id: 2
+ owner_id: 0
+ workflow_id: "test.yaml"
+ index: 192
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
+
+-
+ id: 795
+ title: "to be deleted (test)"
+ repo_id: 2
+ owner_id: 2
+ workflow_id: "test.yaml"
+ index: 191
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
diff --git a/models/fixtures/action_run_job.yml b/models/fixtures/action_run_job.yml
index 9b6f5b9a88..6c06d94aa4 100644
--- a/models/fixtures/action_run_job.yml
+++ b/models/fixtures/action_run_job.yml
@@ -64,7 +64,67 @@
name: job2
attempt: 1
job_id: job2
- needs: [job1]
+ needs: '["job1"]'
+ task_id: 51
+ status: 5
+ started: 1683636528
+ stopped: 1683636626
+
+-
+ id: 198
+ run_id: 795
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job_1
+ attempt: 1
+ job_id: job_1
+ task_id: 53
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+
+-
+ id: 199
+ run_id: 795
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job_2
+ attempt: 1
+ job_id: job_2
+ task_id: 54
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+-
+ id: 203
+ run_id: 802
+ repo_id: 5
+ owner_id: 0
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job2
+ attempt: 1
+ job_id: job2
+ needs: '["job1"]'
+ task_id: 51
+ status: 5
+ started: 1683636528
+ stopped: 1683636626
+-
+ id: 204
+ run_id: 803
+ repo_id: 2
+ owner_id: 0
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job2
+ attempt: 1
+ job_id: job2
+ needs: '["job1"]'
task_id: 51
status: 5
started: 1683636528
diff --git a/models/fixtures/action_runner.yml b/models/fixtures/action_runner.yml
new file mode 100644
index 0000000000..ecb7214006
--- /dev/null
+++ b/models/fixtures/action_runner.yml
@@ -0,0 +1,51 @@
+-
+ id: 34346
+ name: runner_to_be_deleted-user
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
+ version: "1.0.0"
+ owner_id: 1
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34347
+ name: runner_to_be_deleted-org
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
+ version: "1.0.0"
+ owner_id: 3
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34348
+ name: runner_to_be_deleted-repo1
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
+ version: "1.0.0"
+ owner_id: 0
+ repo_id: 1
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34349
+ name: runner_to_be_deleted
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
+ version: "1.0.0"
+ owner_id: 0
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34350
+ name: runner_to_be_deleted-org-ephemeral
+ uuid: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
+ token_hash: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
+ ephemeral: true
+ version: "1.0.0"
+ owner_id: 3
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
diff --git a/models/fixtures/action_task.yml b/models/fixtures/action_task.yml
index 506a47d8a0..c79fb07050 100644
--- a/models/fixtures/action_task.yml
+++ b/models/fixtures/action_task.yml
@@ -117,3 +117,63 @@
log_length: 707
log_size: 90179
log_expired: 0
+-
+ id: 52
+ job_id: 196
+ attempt: 1
+ runner_id: 34350
+ status: 6 # running
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 4
+ owner_id: 1
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: f8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 707
+ log_size: 90179
+ log_expired: 0
+-
+ id: 53
+ job_id: 198
+ attempt: 1
+ runner_id: 1
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784223
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 0
+ log_size: 0
+ log_expired: 0
+-
+ id: 54
+ job_id: 199
+ attempt: 1
+ runner_id: 1
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784224
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 0
+ log_size: 0
+ log_expired: 0
diff --git a/models/fixtures/branch.yml b/models/fixtures/branch.yml
index 17b1869ab6..03e21d04b4 100644
--- a/models/fixtures/branch.yml
+++ b/models/fixtures/branch.yml
@@ -93,3 +93,123 @@
is_deleted: false
deleted_by_id: 0
deleted_unix: 0
+
+-
+ id: 16
+ repo_id: 16
+ name: 'master'
+ commit_id: '69554a64c1e6030f051e5c3f94bfbd773cd6a324'
+ commit_message: 'not signed commit'
+ commit_time: 1502042309
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 17
+ repo_id: 16
+ name: 'not-signed'
+ commit_id: '69554a64c1e6030f051e5c3f94bfbd773cd6a324'
+ commit_message: 'not signed commit'
+ commit_time: 1502042309
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 18
+ repo_id: 16
+ name: 'good-sign-not-yet-validated'
+ commit_id: '27566bd5738fc8b4e3fef3c5e72cce608537bd95'
+ commit_message: 'good signed commit (with not yet validated email)'
+ commit_time: 1502042234
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 19
+ repo_id: 16
+ name: 'good-sign'
+ commit_id: 'f27c2b2b03dcab38beaf89b0ab4ff61f6de63441'
+ commit_message: 'good signed commit'
+ commit_time: 1502042101
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 20
+ repo_id: 1
+ name: 'feature/1'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: 'Initial commit'
+ commit_time: 1489950479
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 21
+ repo_id: 49
+ name: 'master'
+ commit_id: 'aacbdfe9e1c4b47f60abe81849045fa4e96f1d75'
+ commit_message: "Add 'test/test.txt'"
+ commit_time: 1572535577
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 22
+ repo_id: 1
+ name: 'develop'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: "Initial commit"
+ commit_time: 1489927679
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 23
+ repo_id: 3
+ name: 'master'
+ commit_id: '2a47ca4b614a9f5a43abbd5ad851a54a616ffee6'
+ commit_message: "init project"
+ commit_time: 1497448461
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 24
+ repo_id: 3
+ name: 'test_branch'
+ commit_id: 'd22b4d4daa5be07329fcef6ed458f00cf3392da0'
+ commit_message: "test commit"
+ commit_time: 1602935385
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 25
+ repo_id: 54
+ name: 'master'
+ commit_id: '73cf03db6ece34e12bf91e8853dc58f678f2f82d'
+ commit_message: 'Initial commit'
+ commit_time: 1671663402
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
diff --git a/models/fixtures/commit_status.yml b/models/fixtures/commit_status.yml
index 20d57975ef..87c652e53a 100644
--- a/models/fixtures/commit_status.yml
+++ b/models/fixtures/commit_status.yml
@@ -7,6 +7,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
+ context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -18,6 +19,7 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
+ context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -29,6 +31,7 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
+ context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -40,6 +43,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
+ context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -51,4 +55,5 @@
target_url: https://example.com/builds/
description: My awesome deploy service
context: deploy/awesomeness
+ context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2
diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml
index b2a0432635..0f6bd9ee6d 100644
--- a/models/fixtures/email_address.yml
+++ b/models/fixtures/email_address.yml
@@ -81,7 +81,7 @@
-
id: 11
uid: 4
- email: user4@example.com
+ email: User4@Example.Com
lower_email: user4@example.com
is_activated: true
is_primary: true
diff --git a/models/fixtures/hook_task.yml b/models/fixtures/hook_task.yml
index d573406b36..6023719b1e 100644
--- a/models/fixtures/hook_task.yml
+++ b/models/fixtures/hook_task.yml
@@ -18,7 +18,7 @@
id: 2
hook_id: 1
uuid: uuid2
- is_delivered: false
+ is_delivered: true
-
id: 3
diff --git a/models/fixtures/issue_pin.yml b/models/fixtures/issue_pin.yml
new file mode 100644
index 0000000000..14b7a72d84
--- /dev/null
+++ b/models/fixtures/issue_pin.yml
@@ -0,0 +1,6 @@
+-
+ id: 1
+ repo_id: 2
+ issue_id: 4
+ is_pull: false
+ pin_order: 1
diff --git a/models/fixtures/label.yml b/models/fixtures/label.yml
index 2242b90dcd..acfac74968 100644
--- a/models/fixtures/label.yml
+++ b/models/fixtures/label.yml
@@ -96,3 +96,14 @@
num_issues: 0
num_closed_issues: 0
archived_unix: 0
+
+-
+ id: 10
+ repo_id: 3
+ org_id: 0
+ name: repo3label1
+ color: '#112233'
+ exclusive: false
+ num_issues: 0
+ num_closed_issues: 0
+ archived_unix: 0
diff --git a/models/fixtures/protected_tag.yml b/models/fixtures/protected_tag.yml
index dbec52c0c2..1944e7bd84 100644
--- a/models/fixtures/protected_tag.yml
+++ b/models/fixtures/protected_tag.yml
@@ -2,23 +2,23 @@
id: 1
repo_id: 4
name_pattern: /v.+/
- allowlist_user_i_ds: []
- allowlist_team_i_ds: []
+ allowlist_user_i_ds: "[]"
+ allowlist_team_i_ds: "[]"
created_unix: 1715596037
updated_unix: 1715596037
-
id: 2
repo_id: 1
name_pattern: v-*
- allowlist_user_i_ds: []
- allowlist_team_i_ds: []
+ allowlist_user_i_ds: "[]"
+ allowlist_team_i_ds: "[]"
created_unix: 1715596037
updated_unix: 1715596037
-
id: 3
repo_id: 1
name_pattern: v-1.1
- allowlist_user_i_ds: [2]
- allowlist_team_i_ds: []
+ allowlist_user_i_ds: "[2]"
+ allowlist_team_i_ds: "[]"
created_unix: 1715596037
updated_unix: 1715596037
diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml
index db92c95248..b12e6b207f 100644
--- a/models/fixtures/repo_transfer.yml
+++ b/models/fixtures/repo_transfer.yml
@@ -21,3 +21,11 @@
repo_id: 32
created_unix: 1553610671
updated_unix: 1553610671
+
+-
+ id: 4
+ doer_id: 3
+ recipient_id: 1
+ repo_id: 5
+ created_unix: 1553610671
+ updated_unix: 1553610671
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index bbb028eb7b..552a78cbd2 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -1694,19 +1694,6 @@
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
--
- id: 59
- owner_id: 2
- owner_name: user2
- lower_name: test_commit_revert
- name: test_commit_revert
- default_branch: main
- is_empty: false
- is_archived: false
- is_private: true
- status: 0
- num_issues: 0
-
-
id: 60
owner_id: 40
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index b3bece5589..976a236011 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -67,7 +67,7 @@
num_followers: 2
num_following: 1
num_stars: 2
- num_repos: 15
+ num_repos: 14
num_teams: 0
num_members: 0
visibility: 0
diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml
index f62bae1f31..ec282914b8 100644
--- a/models/fixtures/webhook.yml
+++ b/models/fixtures/webhook.yml
@@ -1,7 +1,7 @@
-
id: 1
repo_id: 1
- url: www.example.com/url1
+ url: https://www.example.com/url1
content_type: 1 # json
events: '{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}'
is_active: true
@@ -9,7 +9,7 @@
-
id: 2
repo_id: 1
- url: www.example.com/url2
+ url: https://www.example.com/url2
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: false
@@ -18,14 +18,35 @@
id: 3
owner_id: 3
repo_id: 3
- url: www.example.com/url3
+ url: https://www.example.com/url3
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: true
+
-
id: 4
repo_id: 2
- url: www.example.com/url4
+ url: https://www.example.com/url4
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
+
+-
+ id: 5
+ repo_id: 0
+ owner_id: 0
+ url: https://www.example.com/url5
+ content_type: 1 # json
+ events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
+ is_active: true
+ is_system_webhook: true
+
+-
+ id: 6
+ repo_id: 0
+ owner_id: 0
+ url: https://www.example.com/url6
+ content_type: 1 # json
+ events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
+ is_active: true
+ is_system_webhook: false
diff --git a/models/git/branch.go b/models/git/branch.go
index e683ce47e6..07c94a8ba5 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -167,9 +167,24 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
BranchName: branchName,
}
}
+ // FIXME: this design is not right: it doesn't check `branch.IsDeleted`, it doesn't make sense to make callers to check IsDeleted again and again.
+ // It causes inconsistency with `GetBranches` and `git.GetBranch`, and will lead to strange bugs
+ // In the future, there should be 2 functions: `GetBranchExisting` and `GetBranchWithDeleted`
return &branch, nil
}
+// IsBranchExist returns true if the branch exists in the repository.
+func IsBranchExist(ctx context.Context, repoID int64, branchName string) (bool, error) {
+ var branch Branch
+ has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("name=?", branchName).Get(&branch)
+ if err != nil {
+ return false, err
+ } else if !has {
+ return false, nil
+ }
+ return !branch.IsDeleted, nil
+}
+
func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) {
branches := make([]*Branch, 0, len(branchNames))
@@ -220,6 +235,11 @@ func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch,
return &branch, nil
}
+func DeleteRepoBranches(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id=?", repoID).Delete(new(Branch))
+ return err
+}
+
func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
branches := make([]*Branch, 0, len(branchIDs))
@@ -440,6 +460,8 @@ type FindRecentlyPushedNewBranchesOptions struct {
}
type RecentlyPushedNewBranch struct {
+ BranchRepo *repo_model.Repository
+ BranchName string
BranchDisplayName string
BranchLink string
BranchCompareURL string
@@ -465,7 +487,7 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o
ForkFrom: opts.BaseRepo.ID,
Archived: optional.Some(false),
}
- repoCond := repo_model.SearchRepositoryCondition(&repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
+ repoCond := repo_model.SearchRepositoryCondition(repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
if opts.Repo.ID == opts.BaseRepo.ID {
// should also include the base repo's branches
repoCond = repoCond.Or(builder.Eq{"id": opts.BaseRepo.ID})
@@ -540,7 +562,9 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o
branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName)
}
newBranches = append(newBranches, &RecentlyPushedNewBranch{
+ BranchRepo: branch.Repo,
BranchDisplayName: branchDisplayName,
+ BranchName: branch.Name,
BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)),
BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name),
CommitTime: branch.CommitTime,
diff --git a/models/git/branch_test.go b/models/git/branch_test.go
index b8ea663e81..252dcc5690 100644
--- a/models/git/branch_test.go
+++ b/models/git/branch_test.go
@@ -21,7 +21,7 @@ import (
func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- assert.EqualValues(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
+ assert.Equal(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
assert.True(t, firstBranch.IsDeleted)
diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index 0579a41209..f85e1b15e5 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -17,10 +17,10 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
@@ -30,17 +30,17 @@ import (
// CommitStatus holds a single Status of a single Commit
type CommitStatus struct {
- ID int64 `xorm:"pk autoincr"`
- Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
- RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
- Repo *repo_model.Repository `xorm:"-"`
- State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
- SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
- TargetURL string `xorm:"TEXT"`
- Description string `xorm:"TEXT"`
- ContextHash string `xorm:"VARCHAR(64) index"`
- Context string `xorm:"TEXT"`
- Creator *user_model.User `xorm:"-"`
+ ID int64 `xorm:"pk autoincr"`
+ Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
+ RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
+ Repo *repo_model.Repository `xorm:"-"`
+ State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+ SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
+ TargetURL string `xorm:"TEXT"`
+ Description string `xorm:"TEXT"`
+ ContextHash string `xorm:"VARCHAR(64) index"`
+ Context string `xorm:"TEXT"`
+ Creator *user_model.User `xorm:"-"`
CreatorID int64
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
@@ -222,7 +222,7 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
}
}
- prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
+ prefix := status.Repo.Link() + "/actions"
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
@@ -230,22 +230,25 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
- var lastStatus *CommitStatus
- state := api.CommitStatusSuccess
+ if len(statuses) == 0 {
+ return nil
+ }
+
+ states := make(commitstatus.CommitStatusStates, 0, len(statuses))
+ targetURL := ""
for _, status := range statuses {
- if status.State.NoBetterThan(state) {
- state = status.State
- lastStatus = status
+ states = append(states, status.State)
+ if status.TargetURL != "" {
+ targetURL = status.TargetURL
}
}
- if lastStatus == nil {
- if len(statuses) > 0 {
- lastStatus = statuses[0]
- } else {
- lastStatus = &CommitStatus{}
- }
+
+ return &CommitStatus{
+ RepoID: statuses[0].RepoID,
+ SHA: statuses[0].SHA,
+ State: states.Combine(),
+ TargetURL: targetURL,
}
- return lastStatus
}
// CommitStatusOptions holds the options for query commit statuses
@@ -298,27 +301,37 @@ type CommitStatusIndex struct {
MaxIndex int64 `xorm:"index"`
}
+func makeRepoCommitQuery(ctx context.Context, repoID int64, sha string) *xorm.Session {
+ return db.GetEngine(ctx).Table(&CommitStatus{}).
+ Where("repo_id = ?", repoID).And("sha = ?", sha)
+}
+
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
-func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, int64, error) {
- getBase := func() *xorm.Session {
- return db.GetEngine(ctx).Table(&CommitStatus{}).
- Where("repo_id = ?", repoID).And("sha = ?", sha)
- }
+func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, error) {
indices := make([]int64, 0, 10)
- sess := getBase().Select("max( `index` ) as `index`").
- GroupBy("context_hash").OrderBy("max( `index` ) desc")
+ sess := makeRepoCommitQuery(ctx, repoID, sha).
+ Select("max( `index` ) as `index`").
+ GroupBy("context_hash").
+ OrderBy("max( `index` ) desc")
if !listOptions.IsListAll() {
sess = db.SetSessionPagination(sess, &listOptions)
}
- count, err := sess.FindAndCount(&indices)
- if err != nil {
- return nil, count, err
+ if err := sess.Find(&indices); err != nil {
+ return nil, err
}
statuses := make([]*CommitStatus, 0, len(indices))
if len(indices) == 0 {
- return statuses, count, nil
+ return statuses, nil
}
- return statuses, count, getBase().And(builder.In("`index`", indices)).Find(&statuses)
+ err := makeRepoCommitQuery(ctx, repoID, sha).And(builder.In("`index`", indices)).Find(&statuses)
+ return statuses, err
+}
+
+func CountLatestCommitStatus(ctx context.Context, repoID int64, sha string) (int64, error) {
+ return makeRepoCommitQuery(ctx, repoID, sha).
+ Select("count(context_hash)").
+ GroupBy("context_hash").
+ Count()
}
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
@@ -453,9 +466,8 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
return fmt.Errorf("NewCommitStatus[nil, %s]: no repository specified", opts.SHA)
}
- repoPath := opts.Repo.RepoPath()
if opts.Creator == nil {
- return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
+ return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
}
ctx, committer, err := db.TxContext(ctx)
@@ -477,13 +489,13 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx
- log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
+ log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
// Insert new CommitStatus
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil {
- return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoPath, opts.SHA, err)
+ return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
}
return committer.Commit()
@@ -496,47 +508,11 @@ type SignCommitWithStatuses struct {
*asymkey_model.SignCommit
}
-// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
-func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) []*SignCommitWithStatuses {
- newCommits := make([]*SignCommitWithStatuses, 0, len(oldCommits))
-
- for _, c := range oldCommits {
- commit := &SignCommitWithStatuses{
- SignCommit: c,
- }
- statuses, _, err := GetLatestCommitStatus(ctx, repo.ID, commit.ID.String(), db.ListOptions{})
- if err != nil {
- log.Error("GetLatestCommitStatus: %v", err)
- } else {
- commit.Statuses = statuses
- commit.Status = CalcCommitStatus(statuses)
- }
-
- newCommits = append(newCommits, commit)
- }
- return newCommits
-}
-
// hashCommitStatusContext hash context
func hashCommitStatusContext(context string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
}
-// ConvertFromGitCommit converts git commits into SignCommitWithStatuses
-func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
- return ParseCommitsWithStatus(ctx,
- asymkey_model.ParseCommitsWithSignature(
- ctx,
- user_model.ValidateCommitsWithEmails(ctx, commits),
- repo.GetTrustModel(),
- func(user *user_model.User) (bool, error) {
- return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
- },
- ),
- repo,
- )
-}
-
// CommitStatusesHideActionsURL hide Gitea Actions urls
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
idToRepos := make(map[int64]*repo_model.Repository)
diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go
index 7603e7aa65..dd416fa015 100644
--- a/models/git/commit_status_summary.go
+++ b/models/git/commit_status_summary.go
@@ -7,19 +7,19 @@ import (
"context"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
)
// CommitStatusSummary holds the latest commit Status of a single Commit
type CommitStatusSummary struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
- SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
- State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
- TargetURL string `xorm:"TEXT"`
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
+ SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
+ State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+ TargetURL string `xorm:"TEXT"`
}
func init() {
@@ -55,11 +55,15 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA
}
func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error {
- commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
+ commitStatuses, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
if err != nil {
return err
}
- state := CalcCommitStatus(commitStatuses)
+ // it guarantees that commitStatuses is not empty because this function is always called after a commit status is created
+ if len(commitStatuses) == 0 {
+ setting.PanicInDevOrTesting("no commit statuses found for repo %d and sha %s", repoID, sha)
+ }
+ state := CalcCommitStatus(commitStatuses) // non-empty commitStatuses is guaranteed
// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
// so we need to use insert in on duplicate
if setting.Database.Type.IsMySQL() {
diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go
index 37d785e938..4c0f5e891b 100644
--- a/models/git/commit_status_test.go
+++ b/models/git/commit_status_test.go
@@ -14,9 +14,9 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -26,7 +26,7 @@ func TestGetCommitStatuses(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- sha1 := "1234123412341234123412341234123412341234"
+ sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
@@ -38,23 +38,23 @@ func TestGetCommitStatuses(t *testing.T) {
assert.Len(t, statuses, 5)
assert.Equal(t, "ci/awesomeness", statuses[0].Context)
- assert.Equal(t, structs.CommitStatusPending, statuses[0].State)
+ assert.Equal(t, commitstatus.CommitStatusPending, statuses[0].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[1].Context)
- assert.Equal(t, structs.CommitStatusWarning, statuses[1].State)
+ assert.Equal(t, commitstatus.CommitStatusWarning, statuses[1].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[2].Context)
- assert.Equal(t, structs.CommitStatusSuccess, statuses[2].State)
+ assert.Equal(t, commitstatus.CommitStatusSuccess, statuses[2].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL(db.DefaultContext))
assert.Equal(t, "ci/awesomeness", statuses[3].Context)
- assert.Equal(t, structs.CommitStatusFailure, statuses[3].State)
+ assert.Equal(t, commitstatus.CommitStatusFailure, statuses[3].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[3].APIURL(db.DefaultContext))
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
- assert.Equal(t, structs.CommitStatusError, statuses[4].State)
+ assert.Equal(t, commitstatus.CommitStatusError, statuses[4].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
@@ -75,110 +75,110 @@ func Test_CalcCommitStatus(t *testing.T) {
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusError,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusFailure,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusWarning,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusFailure,
+ State: commitstatus.CommitStatusFailure,
},
{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusError,
},
{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusWarning,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusFailure,
},
},
}
for _, kase := range kases {
- assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
+ assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses), "statuses: %v", kase.statuses)
}
}
@@ -208,7 +208,7 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
Creator: user2,
SHA: commit.ID,
CommitStatus: &git_model.CommitStatus{
- State: structs.CommitStatusFailure,
+ State: commitstatus.CommitStatusFailure,
TargetURL: "https://example.com/tests/",
Context: "compliance/lint-backend",
},
@@ -220,7 +220,7 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
Creator: user2,
SHA: commit.ID,
CommitStatus: &git_model.CommitStatus{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "https://example.com/tests/",
Context: "compliance/lint-backend",
},
@@ -256,3 +256,26 @@ func TestCommitStatusesHideActionsURL(t *testing.T) {
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}
+
+func TestGetCountLatestCommitStatus(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
+
+ commitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo1.ID, sha1, db.ListOptions{
+ Page: 1,
+ PageSize: 2,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, commitStatuses, 2)
+ assert.Equal(t, commitstatus.CommitStatusFailure, commitStatuses[0].State)
+ assert.Equal(t, "ci/awesomeness", commitStatuses[0].Context)
+ assert.Equal(t, commitstatus.CommitStatusError, commitStatuses[1].State)
+ assert.Equal(t, "deploy/awesomeness", commitStatuses[1].Context)
+
+ count, err := git_model.CountLatestCommitStatus(db.DefaultContext, repo1.ID, sha1)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 3, count)
+}
diff --git a/models/git/lfs.go b/models/git/lfs.go
index bb6361050a..e4fa2b446a 100644
--- a/models/git/lfs.go
+++ b/models/git/lfs.go
@@ -112,7 +112,6 @@ type LFSMetaObject struct {
ID int64 `xorm:"pk autoincr"`
lfs.Pointer `xorm:"extends"`
RepositoryID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
- Existing bool `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
@@ -146,7 +145,6 @@ func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMet
if err != nil {
return nil, err
} else if exist {
- m.Existing = true
return m, committer.Commit()
}
diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go
index a3caed73c4..55bbe6938c 100644
--- a/models/git/protected_branch.go
+++ b/models/git/protected_branch.go
@@ -246,7 +246,7 @@ func (protectBranch *ProtectedBranch) GetUnprotectedFilePatterns() []glob.Glob {
func getFilePatterns(filePatterns string) []glob.Glob {
extarr := make([]glob.Glob, 0, 10)
- for _, expr := range strings.Split(strings.ToLower(filePatterns), ";") {
+ for expr := range strings.SplitSeq(strings.ToLower(filePatterns), ";") {
expr = strings.TrimSpace(expr)
if expr != "" {
if g, err := glob.Compile(expr, '.', '/'); err != nil {
@@ -518,7 +518,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre
return currentWhitelist, nil
}
- teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v", repo.OwnerID, repo.ID, err)
}
diff --git a/models/git/protected_branch_list_test.go b/models/git/protected_branch_list_test.go
index a46402c543..298c2fa074 100644
--- a/models/git/protected_branch_list_test.go
+++ b/models/git/protected_branch_list_test.go
@@ -70,7 +70,7 @@ func TestBranchRuleMatchPriority(t *testing.T) {
assert.Error(t, fmt.Errorf("no matched rules but expected %s[%d]", kase.Rules[kase.ExpectedMatchIdx], kase.ExpectedMatchIdx))
}
} else {
- assert.EqualValues(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
+ assert.Equal(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
}
}
}
diff --git a/models/git/protected_branch_test.go b/models/git/protected_branch_test.go
index e1c91d927d..367992081d 100644
--- a/models/git/protected_branch_test.go
+++ b/models/git/protected_branch_test.go
@@ -74,7 +74,7 @@ func TestBranchRuleMatch(t *testing.T) {
} else {
infact = " not"
}
- assert.EqualValues(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
+ assert.Equal(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
"%s should%s match %s but it is%s", kase.BranchName, should, kase.Rule, infact,
)
}
diff --git a/models/issues/comment.go b/models/issues/comment.go
index e4537aa872..9bef96d0dd 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -9,6 +9,7 @@ import (
"context"
"fmt"
"html/template"
+ "slices"
"strconv"
"unicode/utf8"
@@ -19,8 +20,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/references"
@@ -112,8 +111,8 @@ const (
CommentTypePRScheduledToAutoMerge // 34 pr was scheduled to auto merge when checks succeed
CommentTypePRUnScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed
- CommentTypePin // 36 pin Issue
- CommentTypeUnpin // 37 unpin Issue
+ CommentTypePin // 36 pin Issue/PullRequest
+ CommentTypeUnpin // 37 unpin Issue/PullRequest
CommentTypeChangeTimeEstimate // 38 Change time estimate
)
@@ -197,6 +196,15 @@ func (t CommentType) HasMailReplySupport() bool {
return false
}
+func (t CommentType) CountedAsConversation() bool {
+ return slices.Contains(ConversationCountedCommentType(), t)
+}
+
+// ConversationCountedCommentType returns the comment types that are counted as a conversation
+func ConversationCountedCommentType() []CommentType {
+ return []CommentType{CommentTypeComment, CommentTypeReview}
+}
+
// RoleInRepo presents the user's participation in the repo
type RoleInRepo string
@@ -592,26 +600,26 @@ func (c *Comment) LoadAttachments(ctx context.Context) error {
return nil
}
-// UpdateAttachments update attachments by UUIDs for the comment
-func (c *Comment) UpdateAttachments(ctx context.Context, uuids []string) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
+// UpdateCommentAttachments update attachments by UUIDs for the comment
+func UpdateCommentAttachments(ctx context.Context, c *Comment, uuids []string) error {
+ if len(uuids) == 0 {
+ return nil
}
- defer committer.Close()
-
- attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
- if err != nil {
- return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
- }
- for i := 0; i < len(attachments); i++ {
- attachments[i].IssueID = c.IssueID
- attachments[i].CommentID = c.ID
- if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
- return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
+ if err != nil {
+ return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
- }
- return committer.Commit()
+ for i := range attachments {
+ attachments[i].IssueID = c.IssueID
+ attachments[i].CommentID = c.ID
+ if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
+ return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
+ }
+ }
+ c.Attachments = attachments
+ return nil
+ })
}
// LoadAssigneeUserAndTeam if comment.Type is CommentTypeAssignees, then load assignees
@@ -760,41 +768,6 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
return fmt.Sprintf("%s/files#%s", c.Issue.Link(), c.HashTag())
}
-// LoadPushCommits Load push commits
-func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
- if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullRequestPush {
- return nil
- }
-
- var data PushActionContent
-
- err = json.Unmarshal([]byte(c.Content), &data)
- if err != nil {
- return err
- }
-
- c.IsForcePush = data.IsForcePush
-
- if c.IsForcePush {
- if len(data.CommitIDs) != 2 {
- return nil
- }
- c.OldCommit = data.CommitIDs[0]
- c.NewCommit = data.CommitIDs[1]
- } else {
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo)
- if err != nil {
- return err
- }
- defer closer.Close()
-
- c.Commits = git_model.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
- c.CommitsNum = int64(len(c.Commits))
- }
-
- return err
-}
-
// CreateComment creates comment with context
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
ctx, committer, err := db.TxContext(ctx)
@@ -878,7 +851,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
// Check comment type.
switch opts.Type {
case CommentTypeCode:
- if err = updateAttachments(ctx, opts, comment); err != nil {
+ if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
return err
}
if comment.ReviewID != 0 {
@@ -893,12 +866,12 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
}
fallthrough
case CommentTypeComment:
- if _, err = db.Exec(ctx, "UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
+ if err := UpdateIssueNumComments(ctx, opts.Issue.ID); err != nil {
return err
}
fallthrough
case CommentTypeReview:
- if err = updateAttachments(ctx, opts, comment); err != nil {
+ if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
return err
}
case CommentTypeReopen, CommentTypeClose:
@@ -910,23 +883,6 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
return UpdateIssueCols(ctx, opts.Issue, "updated_unix")
}
-func updateAttachments(ctx context.Context, opts *CreateCommentOptions, comment *Comment) error {
- attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, opts.Attachments)
- if err != nil {
- return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", opts.Attachments, err)
- }
- for i := range attachments {
- attachments[i].IssueID = opts.Issue.ID
- attachments[i].CommentID = comment.ID
- // No assign value could be 0, so ignore AllCols().
- if _, err = db.GetEngine(ctx).ID(attachments[i].ID).Update(attachments[i]); err != nil {
- return fmt.Errorf("update attachment [%d]: %w", attachments[i].ID, err)
- }
- }
- comment.Attachments = attachments
- return nil
-}
-
func createDeadlineComment(ctx context.Context, doer *user_model.User, issue *Issue, newDeadlineUnix timeutil.TimeStamp) (*Comment, error) {
var content string
var commentType CommentType
@@ -1182,8 +1138,8 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
return err
}
- if comment.Type == CommentTypeComment {
- if _, err := e.ID(comment.IssueID).Decr("num_comments").Update(new(Issue)); err != nil {
+ if comment.Type.CountedAsConversation() {
+ if err := UpdateIssueNumComments(ctx, comment.IssueID); err != nil {
return err
}
}
@@ -1300,6 +1256,21 @@ func (c *Comment) HasOriginalAuthor() bool {
return c.OriginalAuthor != "" && c.OriginalAuthorID != 0
}
+func UpdateIssueNumCommentsBuilder(issueID int64) *builder.Builder {
+ subQuery := builder.Select("COUNT(*)").From("`comment`").Where(
+ builder.Eq{"issue_id": issueID}.And(
+ builder.In("`type`", ConversationCountedCommentType()),
+ ))
+
+ return builder.Update(builder.Eq{"num_comments": subQuery}).
+ From("`issue`").Where(builder.Eq{"id": issueID})
+}
+
+func UpdateIssueNumComments(ctx context.Context, issueID int64) error {
+ _, err := db.GetEngine(ctx).Exec(UpdateIssueNumCommentsBuilder(issueID))
+ return err
+}
+
// InsertIssueComments inserts many comments of issues.
func InsertIssueComments(ctx context.Context, comments []*Comment) error {
if len(comments) == 0 {
@@ -1332,8 +1303,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
}
for _, issueID := range issueIDs {
- if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?",
- issueID, CommentTypeComment, issueID); err != nil {
+ if err := UpdateIssueNumComments(ctx, issueID); err != nil {
return err
}
}
diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go
index 67a77ceb13..55e67a1243 100644
--- a/models/issues/comment_code.go
+++ b/models/issues/comment_code.go
@@ -5,6 +5,7 @@ package issues
import (
"context"
+ "strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/renderhelper"
@@ -86,8 +87,10 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
ids = append(ids, comment.ReviewID)
}
}
- if err := e.In("id", ids).Find(&reviews); err != nil {
- return nil, err
+ if len(ids) > 0 {
+ if err := e.In("id", ids).Find(&reviews); err != nil {
+ return nil, err
+ }
}
n := 0
@@ -112,7 +115,9 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
}
var err error
- rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(comment.ID, 10),
+ })
if comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content); err != nil {
return nil, err
}
diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go
index 61ac1c8f56..f6c485449f 100644
--- a/models/issues/comment_list.go
+++ b/models/issues/comment_list.go
@@ -26,14 +26,14 @@ func (comments CommentList) LoadPosters(ctx context.Context) error {
return c.PosterID, c.Poster == nil && c.PosterID > 0
})
- posterMaps, err := getPostersByIDs(ctx, posterIDs)
+ posterMaps, err := user_model.GetUsersMapByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, comment := range comments {
if comment.Poster == nil {
- comment.Poster = getPoster(comment.PosterID, posterMaps)
+ comment.Poster = user_model.GetPossibleUserFromMap(comment.PosterID, posterMaps)
}
}
return nil
@@ -41,7 +41,7 @@ func (comments CommentList) LoadPosters(ctx context.Context) error {
func (comments CommentList) getLabelIDs() []int64 {
return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
- return comment.LabelID, comment.LabelID > 0
+ return comment.LabelID, comment.LabelID > 0 && comment.Label == nil
})
}
@@ -51,13 +51,13 @@ func (comments CommentList) loadLabels(ctx context.Context) error {
}
labelIDs := comments.getLabelIDs()
+ if len(labelIDs) == 0 {
+ return nil
+ }
commentLabels := make(map[int64]*Label, len(labelIDs))
left := len(labelIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", labelIDs[:limit]).
Rows(new(Label))
@@ -104,10 +104,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -118,8 +115,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:]
}
- for _, issue := range comments {
- issue.Milestone = milestoneMaps[issue.MilestoneID]
+ for _, comment := range comments {
+ comment.Milestone = milestoneMaps[comment.MilestoneID]
}
return nil
}
@@ -143,10 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -175,13 +169,13 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
}
assigneeIDs := comments.getAssigneeIDs()
+ if len(assigneeIDs) == 0 {
+ return nil
+ }
assignees := make(map[int64]*user_model.User, len(assigneeIDs))
left := len(assigneeIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", assigneeIDs[:limit]).
Rows(new(user_model.User))
@@ -250,10 +244,7 @@ func (comments CommentList) LoadIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -301,13 +292,13 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
e := db.GetEngine(ctx)
issueIDs := comments.getDependentIssueIDs()
+ if len(issueIDs) == 0 {
+ return nil
+ }
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := e.
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -383,10 +374,7 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
commentsIDs := comments.getAttachmentCommentIDs()
left := len(commentsIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("comment_id", commentsIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -427,6 +415,9 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
}
reviewIDs := comments.getReviewIDs()
+ if len(reviewIDs) == 0 {
+ return nil
+ }
reviews := make(map[int64]*Review, len(reviewIDs))
if err := db.GetEngine(ctx).In("id", reviewIDs).Find(&reviews); err != nil {
return err
diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go
index d81f33f953..c08e3b970d 100644
--- a/models/issues/comment_test.go
+++ b/models/issues/comment_test.go
@@ -34,10 +34,10 @@ func TestCreateComment(t *testing.T) {
assert.NoError(t, err)
then := time.Now().Unix()
- assert.EqualValues(t, issues_model.CommentTypeComment, comment.Type)
- assert.EqualValues(t, "Hello", comment.Content)
- assert.EqualValues(t, issue.ID, comment.IssueID)
- assert.EqualValues(t, doer.ID, comment.PosterID)
+ assert.Equal(t, issues_model.CommentTypeComment, comment.Type)
+ assert.Equal(t, "Hello", comment.Content)
+ assert.Equal(t, issue.ID, comment.IssueID)
+ assert.Equal(t, doer.ID, comment.PosterID)
unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB
@@ -45,6 +45,24 @@ func TestCreateComment(t *testing.T) {
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
+func Test_UpdateCommentAttachment(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
+ attachment := repo_model.Attachment{
+ Name: "test.txt",
+ }
+ assert.NoError(t, db.Insert(db.DefaultContext, &attachment))
+
+ err := issues_model.UpdateCommentAttachments(db.DefaultContext, comment, []string{attachment.UUID})
+ assert.NoError(t, err)
+
+ attachment2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: attachment.ID})
+ assert.Equal(t, attachment.Name, attachment2.Name)
+ assert.Equal(t, comment.ID, attachment2.CommentID)
+ assert.Equal(t, comment.IssueID, attachment2.IssueID)
+}
+
func TestFetchCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -93,7 +111,16 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, err)
issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
+ assert.Equal(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
}
+
+func Test_UpdateIssueNumComments(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+
+ assert.NoError(t, issues_model.UpdateIssueNumComments(db.DefaultContext, issue2.ID))
+ issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ assert.Equal(t, 1, issue2.NumComments)
+}
diff --git a/models/issues/dependency_test.go b/models/issues/dependency_test.go
index 6eed483cc9..67418039de 100644
--- a/models/issues/dependency_test.go
+++ b/models/issues/dependency_test.go
@@ -49,9 +49,13 @@ func TestCreateIssueDependency(t *testing.T) {
assert.False(t, left)
// Close #2 and check again
- _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true)
+ _, err = issues_model.CloseIssue(db.DefaultContext, issue2, user1)
assert.NoError(t, err)
+ issue2Closed, err := issues_model.GetIssueByID(db.DefaultContext, 2)
+ assert.NoError(t, err)
+ assert.True(t, issue2Closed.IsClosed)
+
left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
assert.True(t, left)
@@ -59,4 +63,11 @@ func TestCreateIssueDependency(t *testing.T) {
// Test removing the dependency
err = issues_model.RemoveIssueDependency(db.DefaultContext, user1, issue1, issue2, issues_model.DependencyTypeBlockedBy)
assert.NoError(t, err)
+
+ _, err = issues_model.ReopenIssue(db.DefaultContext, issue2, user1)
+ assert.NoError(t, err)
+
+ issue2Reopened, err := issues_model.GetIssueByID(db.DefaultContext, 2)
+ assert.NoError(t, err)
+ assert.False(t, issue2Reopened.IsClosed)
}
diff --git a/models/issues/issue.go b/models/issues/issue.go
index fe347c2715..a86d50ca9d 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -10,6 +10,7 @@ import (
"html/template"
"regexp"
"slices"
+ "strconv"
"code.gitea.io/gitea/models/db"
project_model "code.gitea.io/gitea/models/project"
@@ -17,6 +18,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
@@ -46,23 +48,6 @@ func (err ErrIssueNotExist) Unwrap() error {
return util.ErrNotExist
}
-// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
-type ErrIssueIsClosed struct {
- ID int64
- RepoID int64
- Index int64
-}
-
-// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
-func IsErrIssueIsClosed(err error) bool {
- _, ok := err.(ErrIssueIsClosed)
- return ok
-}
-
-func (err ErrIssueIsClosed) Error() string {
- return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
-}
-
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
type ErrNewIssueInsert struct {
OriginalError error
@@ -78,22 +63,6 @@ func (err ErrNewIssueInsert) Error() string {
return err.OriginalError.Error()
}
-// ErrIssueWasClosed is used when close a closed issue
-type ErrIssueWasClosed struct {
- ID int64
- Index int64
-}
-
-// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
-func IsErrIssueWasClosed(err error) bool {
- _, ok := err.(ErrIssueWasClosed)
- return ok
-}
-
-func (err ErrIssueWasClosed) Error() string {
- return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
-}
-
var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
// Issue represents an issue or pull request of repository.
@@ -129,7 +98,7 @@ type Issue struct {
// TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl"
Ref string
- PinOrder int `xorm:"DEFAULT 0"`
+ PinOrder int `xorm:"-"` // 0 means not loaded, -1 means loaded but not pinned
DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"`
@@ -271,6 +240,9 @@ func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err
IssueID: issue.ID,
Type: tp,
})
+ for _, comment := range issue.Comments {
+ comment.Issue = issue
+ }
return err
}
@@ -320,6 +292,23 @@ func (issue *Issue) LoadMilestone(ctx context.Context) (err error) {
return nil
}
+func (issue *Issue) LoadPinOrder(ctx context.Context) error {
+ if issue.PinOrder != 0 {
+ return nil
+ }
+ issuePin, err := GetIssuePin(ctx, issue)
+ if err != nil && !db.IsErrNotExist(err) {
+ return err
+ }
+
+ if issuePin != nil {
+ issue.PinOrder = issuePin.PinOrder
+ } else {
+ issue.PinOrder = -1
+ }
+ return nil
+}
+
// LoadAttributes loads the attribute of this issue.
func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
if err = issue.LoadRepo(ctx); err != nil {
@@ -359,6 +348,10 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return err
}
+ if err = issue.LoadPinOrder(ctx); err != nil {
+ return err
+ }
+
if err = issue.Comments.LoadAttributes(ctx); err != nil {
return err
}
@@ -371,6 +364,14 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return issue.loadReactions(ctx)
}
+// IsPinned returns if a Issue is pinned
+func (issue *Issue) IsPinned() bool {
+ if issue.PinOrder == 0 {
+ setting.PanicInDevOrTesting("issue's pinorder has not been loaded")
+ }
+ return issue.PinOrder > 0
+}
+
func (issue *Issue) ResetAttributesLoaded() {
issue.isLabelsLoaded = false
issue.isMilestoneLoaded = false
@@ -531,6 +532,45 @@ func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
return issue, nil
}
+func isPullToCond(isPull optional.Option[bool]) builder.Cond {
+ if isPull.Has() {
+ return builder.Eq{"is_pull": isPull.Value()}
+ }
+ return builder.NewCond()
+}
+
+func FindLatestUpdatedIssues(ctx context.Context, repoID int64, isPull optional.Option[bool], pageSize int) (IssueList, error) {
+ issues := make([]*Issue, 0, pageSize)
+ err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
+ And(isPullToCond(isPull)).
+ OrderBy("updated_unix DESC").
+ Limit(pageSize).
+ Find(&issues)
+ return issues, err
+}
+
+func FindIssuesSuggestionByKeyword(ctx context.Context, repoID int64, keyword string, isPull optional.Option[bool], excludedID int64, pageSize int) (IssueList, error) {
+ cond := builder.NewCond()
+ if excludedID > 0 {
+ cond = cond.And(builder.Neq{"`id`": excludedID})
+ }
+
+ // It seems that GitHub searches both title and content (maybe sorting by the search engine's ranking system?)
+ // The first PR (https://github.com/go-gitea/gitea/pull/32327) uses "search indexer" to search "name(title) + content"
+ // But it seems that searching "content" (especially LIKE by DB engine) generates worse (unusable) results.
+ // So now (https://github.com/go-gitea/gitea/pull/33538) it only searches "name(title)", leave the improvements to the future.
+ cond = cond.And(db.BuildCaseInsensitiveLike("`name`", keyword))
+
+ issues := make([]*Issue, 0, pageSize)
+ err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
+ And(isPullToCond(isPull)).
+ And(cond).
+ OrderBy("updated_unix DESC, `index` DESC").
+ Limit(pageSize).
+ Find(&issues)
+ return issues, err
+}
+
// GetIssueWithAttrsByIndex returns issue by index in a repository.
func GetIssueWithAttrsByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
issue, err := GetIssueByIndex(ctx, repoID, index)
@@ -556,6 +596,9 @@ func GetIssueByID(ctx context.Context, id int64) (*Issue, error) {
// If keepOrder is true, the order of the returned issues will be the same as the given IDs.
func GetIssuesByIDs(ctx context.Context, issueIDs []int64, keepOrder ...bool) (IssueList, error) {
issues := make([]*Issue, 0, len(issueIDs))
+ if len(issueIDs) == 0 {
+ return issues, nil
+ }
if err := db.GetEngine(ctx).In("id", issueIDs).Find(&issues); err != nil {
return nil, err
@@ -710,190 +753,6 @@ func (issue *Issue) HasOriginalAuthor() bool {
return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0
}
-var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
-
-// IsPinned returns if a Issue is pinned
-func (issue *Issue) IsPinned() bool {
- return issue.PinOrder != 0
-}
-
-// Pin pins a Issue
-func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error {
- // If the Issue is already pinned, we don't need to pin it twice
- if issue.IsPinned() {
- return nil
- }
-
- var maxPin int
- _, err := db.GetEngine(ctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", issue.RepoID, issue.IsPull).Get(&maxPin)
- if err != nil {
- return err
- }
-
- // Check if the maximum allowed Pins reached
- if maxPin >= setting.Repository.Issue.MaxPinned {
- return ErrIssueMaxPinReached
- }
-
- _, err = db.GetEngine(ctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": maxPin + 1,
- })
- if err != nil {
- return err
- }
-
- // Add the pin event to the history
- opts := &CreateCommentOptions{
- Type: CommentTypePin,
- Doer: user,
- Repo: issue.Repo,
- Issue: issue,
- }
- if _, err = CreateComment(ctx, opts); err != nil {
- return err
- }
-
- return nil
-}
-
-// UnpinIssue unpins a Issue
-func (issue *Issue) Unpin(ctx context.Context, user *user_model.User) error {
- // If the Issue is not pinned, we don't need to unpin it
- if !issue.IsPinned() {
- return nil
- }
-
- // This sets the Pin for all Issues that come after the unpined Issue to the correct value
- _, err := db.GetEngine(ctx).Exec("UPDATE issue SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ?", issue.RepoID, issue.IsPull, issue.PinOrder)
- if err != nil {
- return err
- }
-
- _, err = db.GetEngine(ctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": 0,
- })
- if err != nil {
- return err
- }
-
- // Add the unpin event to the history
- opts := &CreateCommentOptions{
- Type: CommentTypeUnpin,
- Doer: user,
- Repo: issue.Repo,
- Issue: issue,
- }
- if _, err = CreateComment(ctx, opts); err != nil {
- return err
- }
-
- return nil
-}
-
-// PinOrUnpin pins or unpins a Issue
-func (issue *Issue) PinOrUnpin(ctx context.Context, user *user_model.User) error {
- if !issue.IsPinned() {
- return issue.Pin(ctx, user)
- }
-
- return issue.Unpin(ctx, user)
-}
-
-// MovePin moves a Pinned Issue to a new Position
-func (issue *Issue) MovePin(ctx context.Context, newPosition int) error {
- // If the Issue is not pinned, we can't move them
- if !issue.IsPinned() {
- return nil
- }
-
- if newPosition < 1 {
- return fmt.Errorf("The Position can't be lower than 1")
- }
-
- dbctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- var maxPin int
- _, err = db.GetEngine(dbctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", issue.RepoID, issue.IsPull).Get(&maxPin)
- if err != nil {
- return err
- }
-
- // If the new Position bigger than the current Maximum, set it to the Maximum
- if newPosition > maxPin+1 {
- newPosition = maxPin + 1
- }
-
- // Lower the Position of all Pinned Issue that came after the current Position
- _, err = db.GetEngine(dbctx).Exec("UPDATE issue SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ?", issue.RepoID, issue.IsPull, issue.PinOrder)
- if err != nil {
- return err
- }
-
- // Higher the Position of all Pinned Issues that comes after the new Position
- _, err = db.GetEngine(dbctx).Exec("UPDATE issue SET pin_order = pin_order + 1 WHERE repo_id = ? AND is_pull = ? AND pin_order >= ?", issue.RepoID, issue.IsPull, newPosition)
- if err != nil {
- return err
- }
-
- _, err = db.GetEngine(dbctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": newPosition,
- })
- if err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
-// GetPinnedIssues returns the pinned Issues for the given Repo and type
-func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, error) {
- issues := make(IssueList, 0)
-
- err := db.GetEngine(ctx).
- Table("issue").
- Where("repo_id = ?", repoID).
- And("is_pull = ?", isPull).
- And("pin_order > 0").
- OrderBy("pin_order").
- Find(&issues)
- if err != nil {
- return nil, err
- }
-
- err = issues.LoadAttributes(ctx)
- if err != nil {
- return nil, err
- }
-
- return issues, nil
-}
-
-// IsNewPinAllowed returns if a new Issue or Pull request can be pinned
-func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
- var maxPin int
- _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
- if err != nil {
- return false, err
- }
-
- return maxPin < setting.Repository.Issue.MaxPinned, nil
-}
-
-// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
-func IsErrIssueMaxPinReached(err error) bool {
- return err == ErrIssueMaxPinReached
-}
-
// InsertIssues insert issues to database
func InsertIssues(ctx context.Context, issues ...*Issue) error {
ctx, committer, err := db.TxContext(ctx)
@@ -957,7 +816,7 @@ func ChangeIssueTimeEstimate(ctx context.Context, issue *Issue, doer *user_model
Doer: doer,
Repo: issue.Repo,
Issue: issue,
- Content: fmt.Sprintf("%d", timeEstimate),
+ Content: strconv.FormatInt(timeEstimate, 10),
}
if _, err := CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
diff --git a/models/issues/issue_label.go b/models/issues/issue_label.go
index 10fc821454..f082079e07 100644
--- a/models/issues/issue_label.go
+++ b/models/issues/issue_label.go
@@ -206,6 +206,7 @@ func DeleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use
}
issue.Labels = nil
+ issue.isLabelsLoaded = false
return issue.LoadLabels(ctx)
}
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 22a4548adc..26b93189b8 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -42,10 +42,7 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi
repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Find(&repoMaps)
@@ -81,53 +78,19 @@ func (issues IssueList) LoadPosters(ctx context.Context) error {
return issue.PosterID, issue.Poster == nil && issue.PosterID > 0
})
- posterMaps, err := getPostersByIDs(ctx, posterIDs)
+ posterMaps, err := user_model.GetUsersMapByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, issue := range issues {
if issue.Poster == nil {
- issue.Poster = getPoster(issue.PosterID, posterMaps)
+ issue.Poster = user_model.GetPossibleUserFromMap(issue.PosterID, posterMaps)
}
}
return nil
}
-func getPostersByIDs(ctx context.Context, posterIDs []int64) (map[int64]*user_model.User, error) {
- posterMaps := make(map[int64]*user_model.User, len(posterIDs))
- left := len(posterIDs)
- for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
- err := db.GetEngine(ctx).
- In("id", posterIDs[:limit]).
- Find(&posterMaps)
- if err != nil {
- return nil, err
- }
- left -= limit
- posterIDs = posterIDs[limit:]
- }
- return posterMaps, nil
-}
-
-func getPoster(posterID int64, posterMaps map[int64]*user_model.User) *user_model.User {
- if posterID == user_model.ActionsUserID {
- return user_model.NewActionsUser()
- }
- if posterID <= 0 {
- return nil
- }
- poster, ok := posterMaps[posterID]
- if !ok {
- return user_model.NewGhostUser()
- }
- return poster
-}
-
func (issues IssueList) getIssueIDs() []int64 {
ids := make([]int64, 0, len(issues))
for _, issue := range issues {
@@ -150,10 +113,7 @@ func (issues IssueList) LoadLabels(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("label").
Join("LEFT", "issue_label", "issue_label.label_id = label.id").
In("issue_label.issue_id", issueIDs[:limit]).
@@ -205,10 +165,7 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -237,10 +194,7 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
}
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
projects := make([]*projectWithIssueID, 0, limit)
err := db.GetEngine(ctx).
@@ -279,10 +233,7 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("issue_assignees").
Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
In("`issue_assignees`.issue_id", issueIDs[:limit]).OrderBy(user_model.GetOrderByName()).
@@ -340,10 +291,7 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error {
pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(PullRequest))
@@ -388,10 +336,7 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) {
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -433,10 +378,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("comment").
Join("INNER", "issue", "issue.id = comment.issue_id").
In("issue.id", issuesIDs[:limit]).
@@ -500,10 +442,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
left := len(ids)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
// select issue_id, sum(time) from tracked_time where issue_id in () group by issue_id
rows, err := db.GetEngine(ctx).Table("tracked_time").
@@ -540,6 +479,39 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
return nil
}
+func (issues IssueList) LoadPinOrder(ctx context.Context) error {
+ if len(issues) == 0 {
+ return nil
+ }
+
+ issueIDs := container.FilterSlice(issues, func(issue *Issue) (int64, bool) {
+ return issue.ID, issue.PinOrder == 0
+ })
+ if len(issueIDs) == 0 {
+ return nil
+ }
+ issuePins, err := GetIssuePinsByIssueIDs(ctx, issueIDs)
+ if err != nil {
+ return err
+ }
+
+ for _, issue := range issues {
+ if issue.PinOrder != 0 {
+ continue
+ }
+ for _, pin := range issuePins {
+ if pin.IssueID == issue.ID {
+ issue.PinOrder = pin.PinOrder
+ break
+ }
+ }
+ if issue.PinOrder == 0 {
+ issue.PinOrder = -1
+ }
+ }
+ return nil
+}
+
// loadAttributes loads all attributes, expect for attachments and comments
func (issues IssueList) LoadAttributes(ctx context.Context) error {
if _, err := issues.LoadRepositories(ctx); err != nil {
diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go
index 9069e1012d..5b4d2ca5ab 100644
--- a/models/issues/issue_list_test.go
+++ b/models/issues/issue_list_test.go
@@ -27,7 +27,7 @@ func TestIssueList_LoadRepositories(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, repos, 2)
for _, issue := range issueList {
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
}
}
@@ -41,28 +41,28 @@ func TestIssueList_LoadAttributes(t *testing.T) {
assert.NoError(t, issueList.LoadAttributes(db.DefaultContext))
for _, issue := range issueList {
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.EqualValues(t, issue.RepoID, label.RepoID)
+ assert.Equal(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
+ assert.Equal(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
+ assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.EqualValues(t, issue.ID, attachment.IssueID)
+ assert.Equal(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.EqualValues(t, issue.ID, comment.IssueID)
+ assert.Equal(t, issue.ID, comment.IssueID)
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
diff --git a/models/issues/issue_lock.go b/models/issues/issue_lock.go
index b21629b529..fa0d128f74 100644
--- a/models/issues/issue_lock.go
+++ b/models/issues/issue_lock.go
@@ -12,8 +12,14 @@ import (
// IssueLockOptions defines options for locking and/or unlocking an issue/PR
type IssueLockOptions struct {
- Doer *user_model.User
- Issue *Issue
+ Doer *user_model.User
+ Issue *Issue
+
+ // Reason is the doer-provided comment message for the locked issue
+ // GitHub doesn't support changing the "reasons" by config file, so GitHub has pre-defined "reason" enum values.
+ // Gitea is not like GitHub, it allows site admin to define customized "reasons" in the config file.
+ // So the API caller might not know what kind of "reasons" are valid, and the customized reasons are not translatable.
+ // To make things clear and simple: doer have the chance to use any reason they like, we do not do validation.
Reason string
}
diff --git a/models/issues/issue_pin.go b/models/issues/issue_pin.go
new file mode 100644
index 0000000000..ae6195b05d
--- /dev/null
+++ b/models/issues/issue_pin.go
@@ -0,0 +1,246 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package issues
+
+import (
+ "context"
+ "errors"
+ "sort"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type IssuePin struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IssueID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IsPull bool `xorm:"NOT NULL"`
+ PinOrder int `xorm:"DEFAULT 0"`
+}
+
+var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
+
+// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
+func IsErrIssueMaxPinReached(err error) bool {
+ return err == ErrIssueMaxPinReached
+}
+
+func init() {
+ db.RegisterModel(new(IssuePin))
+}
+
+func GetIssuePin(ctx context.Context, issue *Issue) (*IssuePin, error) {
+ pin := new(IssuePin)
+ has, err := db.GetEngine(ctx).
+ Where("repo_id = ?", issue.RepoID).
+ And("issue_id = ?", issue.ID).Get(pin)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, db.ErrNotExist{
+ Resource: "IssuePin",
+ ID: issue.ID,
+ }
+ }
+ return pin, nil
+}
+
+func GetIssuePinsByIssueIDs(ctx context.Context, issueIDs []int64) ([]IssuePin, error) {
+ var pins []IssuePin
+ if err := db.GetEngine(ctx).In("issue_id", issueIDs).Find(&pins); err != nil {
+ return nil, err
+ }
+ return pins, nil
+}
+
+// Pin pins a Issue
+func PinIssue(ctx context.Context, issue *Issue, user *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ pinnedIssuesNum, err := getPinnedIssuesNum(ctx, issue.RepoID, issue.IsPull)
+ if err != nil {
+ return err
+ }
+
+ // Check if the maximum allowed Pins reached
+ if pinnedIssuesNum >= setting.Repository.Issue.MaxPinned {
+ return ErrIssueMaxPinReached
+ }
+
+ pinnedIssuesMaxPinOrder, err := getPinnedIssuesMaxPinOrder(ctx, issue.RepoID, issue.IsPull)
+ if err != nil {
+ return err
+ }
+
+ if _, err = db.GetEngine(ctx).Insert(&IssuePin{
+ RepoID: issue.RepoID,
+ IssueID: issue.ID,
+ IsPull: issue.IsPull,
+ PinOrder: pinnedIssuesMaxPinOrder + 1,
+ }); err != nil {
+ return err
+ }
+
+ // Add the pin event to the history
+ _, err = CreateComment(ctx, &CreateCommentOptions{
+ Type: CommentTypePin,
+ Doer: user,
+ Repo: issue.Repo,
+ Issue: issue,
+ })
+ return err
+ })
+}
+
+// UnpinIssue unpins a Issue
+func UnpinIssue(ctx context.Context, issue *Issue, user *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ // This sets the Pin for all Issues that come after the unpined Issue to the correct value
+ cnt, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Delete(new(IssuePin))
+ if err != nil {
+ return err
+ }
+ if cnt == 0 {
+ return nil
+ }
+
+ // Add the unpin event to the history
+ _, err = CreateComment(ctx, &CreateCommentOptions{
+ Type: CommentTypeUnpin,
+ Doer: user,
+ Repo: issue.Repo,
+ Issue: issue,
+ })
+ return err
+ })
+}
+
+func getPinnedIssuesNum(ctx context.Context, repoID int64, isPull bool) (int, error) {
+ var pinnedIssuesNum int
+ _, err := db.GetEngine(ctx).SQL("SELECT count(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&pinnedIssuesNum)
+ return pinnedIssuesNum, err
+}
+
+func getPinnedIssuesMaxPinOrder(ctx context.Context, repoID int64, isPull bool) (int, error) {
+ var maxPinnedIssuesMaxPinOrder int
+ _, err := db.GetEngine(ctx).SQL("SELECT max(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPinnedIssuesMaxPinOrder)
+ return maxPinnedIssuesMaxPinOrder, err
+}
+
+// MovePin moves a Pinned Issue to a new Position
+func MovePin(ctx context.Context, issue *Issue, newPosition int) error {
+ if newPosition < 1 {
+ return errors.New("The Position can't be lower than 1")
+ }
+
+ issuePin, err := GetIssuePin(ctx, issue)
+ if err != nil {
+ return err
+ }
+ if issuePin.PinOrder == newPosition {
+ return nil
+ }
+
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if issuePin.PinOrder > newPosition { // move the issue to a lower position
+ _, err = db.GetEngine(ctx).Exec("UPDATE issue_pin SET pin_order = pin_order + 1 WHERE repo_id = ? AND is_pull = ? AND pin_order >= ? AND pin_order < ?", issue.RepoID, issue.IsPull, newPosition, issuePin.PinOrder)
+ } else { // move the issue to a higher position
+ // Lower the Position of all Pinned Issue that came after the current Position
+ _, err = db.GetEngine(ctx).Exec("UPDATE issue_pin SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ? AND pin_order <= ?", issue.RepoID, issue.IsPull, issuePin.PinOrder, newPosition)
+ }
+ if err != nil {
+ return err
+ }
+
+ _, err = db.GetEngine(ctx).
+ Table("issue_pin").
+ Where("id = ?", issuePin.ID).
+ Update(map[string]any{
+ "pin_order": newPosition,
+ })
+ return err
+ })
+}
+
+func GetPinnedIssueIDs(ctx context.Context, repoID int64, isPull bool) ([]int64, error) {
+ var issuePins []IssuePin
+ if err := db.GetEngine(ctx).
+ Table("issue_pin").
+ Where("repo_id = ?", repoID).
+ And("is_pull = ?", isPull).
+ Find(&issuePins); err != nil {
+ return nil, err
+ }
+
+ sort.Slice(issuePins, func(i, j int) bool {
+ return issuePins[i].PinOrder < issuePins[j].PinOrder
+ })
+
+ var ids []int64
+ for _, pin := range issuePins {
+ ids = append(ids, pin.IssueID)
+ }
+ return ids, nil
+}
+
+func GetIssuePinsByRepoID(ctx context.Context, repoID int64, isPull bool) ([]*IssuePin, error) {
+ var pins []*IssuePin
+ if err := db.GetEngine(ctx).Where("repo_id = ? AND is_pull = ?", repoID, isPull).Find(&pins); err != nil {
+ return nil, err
+ }
+ return pins, nil
+}
+
+// GetPinnedIssues returns the pinned Issues for the given Repo and type
+func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, error) {
+ issuePins, err := GetIssuePinsByRepoID(ctx, repoID, isPull)
+ if err != nil {
+ return nil, err
+ }
+ if len(issuePins) == 0 {
+ return IssueList{}, nil
+ }
+ ids := make([]int64, 0, len(issuePins))
+ for _, pin := range issuePins {
+ ids = append(ids, pin.IssueID)
+ }
+
+ issues := make(IssueList, 0, len(ids))
+ if err := db.GetEngine(ctx).In("id", ids).Find(&issues); err != nil {
+ return nil, err
+ }
+ for _, issue := range issues {
+ for _, pin := range issuePins {
+ if pin.IssueID == issue.ID {
+ issue.PinOrder = pin.PinOrder
+ break
+ }
+ }
+ if (!setting.IsProd || setting.IsInTesting) && issue.PinOrder == 0 {
+ panic("It should not happen that a pinned Issue has no PinOrder")
+ }
+ }
+ sort.Slice(issues, func(i, j int) bool {
+ return issues[i].PinOrder < issues[j].PinOrder
+ })
+
+ if err = issues.LoadAttributes(ctx); err != nil {
+ return nil, err
+ }
+
+ return issues, nil
+}
+
+// IsNewPinAllowed returns if a new Issue or Pull request can be pinned
+func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
+ var maxPin int
+ _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPin)
+ if err != nil {
+ return false, err
+ }
+
+ return maxPin < setting.Repository.Issue.MaxPinned, nil
+}
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index c4515fd898..0185244783 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -38,13 +38,30 @@ func (issue *Issue) projectID(ctx context.Context) int64 {
}
// ProjectColumnID return project column id if issue was assigned to one
-func (issue *Issue) ProjectColumnID(ctx context.Context) int64 {
+func (issue *Issue) ProjectColumnID(ctx context.Context) (int64, error) {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
- if err != nil || !has {
- return 0
+ if err != nil {
+ return 0, err
+ } else if !has {
+ return 0, nil
}
- return ip.ProjectColumnID
+ return ip.ProjectColumnID, nil
+}
+
+func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID int64) (map[int64]int64, error) {
+ issues := make([]project_model.ProjectIssue, 0)
+ if err := db.GetEngine(ctx).Where("project_id=?", projectID).Find(&issues); err != nil {
+ return nil, err
+ }
+ result := make(map[int64]int64, len(issues))
+ for _, issue := range issues {
+ if issue.ProjectColumnID == 0 {
+ issue.ProjectColumnID = defaultColumnID
+ }
+ result[issue.IssueID] = issue.ProjectColumnID
+ }
+ return result, nil
}
// LoadIssuesFromColumn load issues assigned to this column
@@ -59,11 +76,11 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
}
if b.Default {
- issues, err := Issues(ctx, &IssuesOptions{
- ProjectColumnID: db.NoConditionID,
- ProjectID: b.ProjectID,
- SortType: "project-column-sorting",
- })
+ issues, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
+ o.ProjectColumnID = db.NoConditionID
+ o.ProjectID = b.ProjectID
+ o.SortType = "project-column-sorting"
+ }))
if err != nil {
return nil, err
}
@@ -77,19 +94,6 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
return issueList, nil
}
-// LoadIssuesFromColumnList load issues assigned to the columns
-func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList, opts *IssuesOptions) (map[int64]IssueList, error) {
- issuesMap := make(map[int64]IssueList, len(bs))
- for i := range bs {
- il, err := LoadIssuesFromColumn(ctx, bs[i], opts)
- if err != nil {
- return nil, err
- }
- issuesMap[bs[i].ID] = il
- }
- return issuesMap, nil
-}
-
// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
@@ -110,7 +114,7 @@ func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_mo
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}
if newColumnID == 0 {
- newDefaultColumn, err := newProject.GetDefaultColumn(ctx)
+ newDefaultColumn, err := newProject.MustDefaultColumn(ctx)
if err != nil {
return err
}
diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go
index f1cd125d49..79bd6a19b0 100644
--- a/models/issues/issue_search.go
+++ b/models/issues/issue_search.go
@@ -21,14 +21,16 @@ import (
"xorm.io/xorm"
)
+const ScopeSortPrefix = "scope-"
+
// IssuesOptions represents options of an issue.
-type IssuesOptions struct { //nolint
+type IssuesOptions struct { //nolint:revive // export stutter
Paginator *db.ListOptions
RepoIDs []int64 // overwrites RepoCond if the length is not 0
AllPublic bool // include also all public repositories
RepoCond builder.Cond
- AssigneeID optional.Option[int64]
- PosterID optional.Option[int64]
+ AssigneeID string // "(none)" or "(any)" or a user ID
+ PosterID string // "(none)" or "(any)" or a user ID
MentionedID int64
ReviewRequestedID int64
ReviewedID int64
@@ -49,9 +51,9 @@ type IssuesOptions struct { //nolint
// prioritize issues from this repo
PriorityRepoID int64
IsArchived optional.Option[bool]
- Org *organization.Organization // issues permission scope
- Team *organization.Team // issues permission scope
- User *user_model.User // issues permission scope
+ Owner *user_model.User // issues permission scope, it could be an organization or a user
+ Team *organization.Team // issues permission scope
+ Doer *user_model.User // issues permission scope
}
// Copy returns a copy of the options.
@@ -70,11 +72,24 @@ func (o *IssuesOptions) Copy(edit ...func(options *IssuesOptions)) *IssuesOption
// applySorts sort an issues-related session based on the provided
// sortType string
func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
+ // Since this sortType is dynamically created, it has to be treated specially.
+ if after, ok := strings.CutPrefix(sortType, ScopeSortPrefix); ok {
+ scope := after
+ sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id")
+ // "exclusive_order=0" means "no order is set", so exclude it from the JOIN criteria and then "LEFT JOIN" result is also null
+ sess.Join("LEFT", "label", "label.id = issue_label.label_id AND label.exclusive_order <> 0 AND label.name LIKE ?", scope+"/%")
+ // Use COALESCE to make sure we sort NULL last regardless of backend DB (2147483647 == max int)
+ sess.OrderBy("COALESCE(label.exclusive_order, 2147483647) ASC").Desc("issue.id")
+ return
+ }
+
switch sortType {
case "oldest":
sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
+ case "recentclose":
+ sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":
@@ -273,8 +288,12 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
applyLabelsCondition(sess, opts)
- if opts.User != nil {
- sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()))
+ if opts.Owner != nil {
+ sess.And(repo_model.UserOwnedRepoCond(opts.Owner.ID))
+ }
+
+ if opts.Doer != nil && !opts.Doer.IsAdmin {
+ sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.Doer.ID, opts.Owner, opts.Team, opts.IsPull.Value()))
}
}
@@ -321,20 +340,20 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
}
// issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table
-func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond {
+func issuePullAccessibleRepoCond(repoIDstr string, userID int64, owner *user_model.User, team *organization.Team, isPull bool) builder.Cond {
cond := builder.NewCond()
unitType := unit.TypeIssues
if isPull {
unitType = unit.TypePullRequests
}
- if org != nil {
+ if owner != nil && owner.IsOrganization() {
if team != nil {
- cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, org.ID, team.ID, unitType)) // special team member repos
+ cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, owner.ID, team.ID, unitType)) // special team member repos
} else {
cond = cond.And(
builder.Or(
- repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
- repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
+ repo_model.UserOrgUnitRepoCond(repoIDstr, userID, owner.ID, unitType), // team member repos
+ repo_model.UserOrgPublicUnitRepoCond(userID, owner.ID), // user org public non-member repos, TODO: check repo has issues
),
)
}
@@ -352,26 +371,25 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
return cond
}
-func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) {
+func applyAssigneeCondition(sess *xorm.Session, assigneeID string) {
// old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64
- if !assigneeID.Has() || assigneeID.Value() == 0 {
- return
- }
- if assigneeID.Value() == db.NoConditionID {
+ if assigneeID == "(none)" {
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
- } else {
+ } else if assigneeID == "(any)" {
+ sess.Where("issue.id IN (SELECT issue_id FROM issue_assignees)")
+ } else if assigneeIDInt64, _ := strconv.ParseInt(assigneeID, 10, 64); assigneeIDInt64 > 0 {
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
- And("issue_assignees.assignee_id = ?", assigneeID.Value())
+ And("issue_assignees.assignee_id = ?", assigneeIDInt64)
}
}
-func applyPosterCondition(sess *xorm.Session, posterID optional.Option[int64]) {
- if !posterID.Has() {
- return
- }
- // poster doesn't need to support db.NoConditionID(-1), so just use the value as-is
- if posterID.Has() {
- sess.And("issue.poster_id=?", posterID.Value())
+func applyPosterCondition(sess *xorm.Session, posterID string) {
+ // Actually every issue has a poster.
+ // The "(none)" is for internal usage only: when doer tries to search non-existing user as poster, use "(none)" to return empty result.
+ if posterID == "(none)" {
+ sess.And("issue.poster_id=0")
+ } else if posterIDInt64, _ := strconv.ParseInt(posterID, 10, 64); posterIDInt64 > 0 {
+ sess.And("issue.poster_id=?", posterIDInt64)
}
}
diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go
index 9ef9347a16..adedaa3d3a 100644
--- a/models/issues/issue_stats.go
+++ b/models/issues/issue_stats.go
@@ -94,10 +94,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
// ids in a temporary table and join from them.
accum := &IssueStats{}
for i := 0; i < len(opts.IssueIDs); {
- chunk := i + MaxQueryParameters
- if chunk > len(opts.IssueIDs) {
- chunk = len(opts.IssueIDs)
- }
+ chunk := min(i+MaxQueryParameters, len(opts.IssueIDs))
stats, err := getIssueStatsChunk(ctx, opts, opts.IssueIDs[i:chunk])
if err != nil {
return nil, err
@@ -107,7 +104,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
accum.YourRepositoriesCount += stats.YourRepositoriesCount
accum.AssignCount += stats.AssignCount
accum.CreateCount += stats.CreateCount
- accum.OpenCount += stats.MentionCount
+ accum.MentionCount += stats.MentionCount
accum.ReviewRequestedCount += stats.ReviewRequestedCount
accum.ReviewedCount += stats.ReviewedCount
i = chunk
diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go
index dbbb1e4179..1c5db55bbc 100644
--- a/models/issues/issue_test.go
+++ b/models/issues/issue_test.go
@@ -4,8 +4,8 @@
package issues_test
import (
- "context"
"fmt"
+ "slices"
"sort"
"sync"
"testing"
@@ -16,7 +16,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
@@ -143,8 +142,8 @@ func TestUpdateIssueCols(t *testing.T) {
then := time.Now().Unix()
updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
- assert.EqualValues(t, newTitle, updatedIssue.Title)
- assert.EqualValues(t, prevContent, updatedIssue.Content)
+ assert.Equal(t, newTitle, updatedIssue.Title)
+ assert.Equal(t, prevContent, updatedIssue.Content)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
@@ -156,7 +155,7 @@ func TestIssues(t *testing.T) {
}{
{
issues_model.IssuesOptions{
- AssigneeID: optional.Some(int64(1)),
+ AssigneeID: "1",
SortType: "oldest",
},
[]int64{1, 6},
@@ -203,7 +202,7 @@ func TestIssues(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
for i, issue := range issues {
- assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
+ assert.Equal(t, test.ExpectedIssueIDs[i], issue.ID)
}
}
}
@@ -236,10 +235,10 @@ func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *is
has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, issue.Title, newIssue.Title)
- assert.EqualValues(t, issue.Content, newIssue.Content)
+ assert.Equal(t, issue.Title, newIssue.Title)
+ assert.Equal(t, issue.Content, newIssue.Content)
if expectIndex > 0 {
- assert.EqualValues(t, expectIndex, newIssue.Index)
+ assert.Equal(t, expectIndex, newIssue.Index)
}
})
return &newIssue
@@ -272,8 +271,8 @@ func TestIssue_ResolveMentions(t *testing.T) {
for i, user := range resolved {
ids[i] = user.ID
}
- sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
- assert.EqualValues(t, expected, ids)
+ slices.Sort(ids)
+ assert.Equal(t, expected, ids)
}
// Public repo, existing user
@@ -294,7 +293,7 @@ func TestResourceIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
var wg sync.WaitGroup
- for i := 0; i < 100; i++ {
+ for i := range 100 {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0)
@@ -316,7 +315,7 @@ func TestCorrectIssueStats(t *testing.T) {
issueAmount := issues_model.MaxQueryParameters + 10
var wg sync.WaitGroup
- for i := 0; i < issueAmount; i++ {
+ for i := range issueAmount {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
@@ -326,7 +325,7 @@ func TestCorrectIssueStats(t *testing.T) {
wg.Wait()
// Now we will get all issueID's that match the "Bugs are nasty" query.
- issues, err := issues_model.Issues(context.TODO(), &issues_model.IssuesOptions{
+ issues, err := issues_model.Issues(t.Context(), &issues_model.IssuesOptions{
Paginator: &db.ListOptions{
PageSize: issueAmount,
},
@@ -394,28 +393,28 @@ func TestIssueLoadAttributes(t *testing.T) {
for _, issue := range issueList {
assert.NoError(t, issue.LoadAttributes(db.DefaultContext))
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.EqualValues(t, issue.RepoID, label.RepoID)
+ assert.Equal(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
+ assert.Equal(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
+ assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.EqualValues(t, issue.ID, attachment.IssueID)
+ assert.Equal(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.EqualValues(t, issue.ID, comment.IssueID)
+ assert.Equal(t, issue.ID, comment.IssueID)
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go
index 5b929c9115..9b99787e3b 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -5,16 +5,14 @@ package issues
import (
"context"
+ "errors"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -28,38 +26,40 @@ import (
// UpdateIssueCols updates cols of issue
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
- if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
- return err
- }
- return nil
+ _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue)
+ return err
}
-func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
- // Reload the issue
- currentIssue, err := GetIssueByID(ctx, issue.ID)
- if err != nil {
- return nil, err
- }
+// ErrIssueIsClosed is used when close a closed issue
+type ErrIssueIsClosed struct {
+ ID int64
+ RepoID int64
+ Index int64
+ IsPull bool
+}
- // Nothing should be performed if current status is same as target status
- if currentIssue.IsClosed == isClosed {
- if !issue.IsPull {
- return nil, ErrIssueWasClosed{
- ID: issue.ID,
- }
- }
- return nil, ErrPullWasClosed{
- ID: issue.ID,
+// IsErrIssueIsClosed checks if an error is a ErrIssueIsClosed.
+func IsErrIssueIsClosed(err error) bool {
+ _, ok := err.(ErrIssueIsClosed)
+ return ok
+}
+
+func (err ErrIssueIsClosed) Error() string {
+ return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already closed", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
+}
+
+func SetIssueAsClosed(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
+ if issue.IsClosed {
+ return nil, ErrIssueIsClosed{
+ ID: issue.ID,
+ RepoID: issue.RepoID,
+ Index: issue.Index,
+ IsPull: issue.IsPull,
}
}
- issue.IsClosed = isClosed
- return doChangeIssueStatus(ctx, issue, doer, isMergePull)
-}
-
-func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
// Check for open dependencies
- if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) {
+ if issue.Repo.IsDependenciesEnabled(ctx) {
// only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies
noDeps, err := IssueNoDependenciesLeft(ctx, issue)
if err != nil {
@@ -71,16 +71,63 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
}
}
- if issue.IsClosed {
- issue.ClosedUnix = timeutil.TimeStampNow()
- } else {
- issue.ClosedUnix = 0
- }
+ issue.IsClosed = true
+ issue.ClosedUnix = timeutil.TimeStampNow()
- if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); err != nil {
+ if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
+ Where("is_closed = ?", false).
+ Update(issue); err != nil {
return nil, err
+ } else if cnt != 1 {
+ return nil, ErrIssueAlreadyChanged
}
+ return updateIssueNumbers(ctx, issue, doer, util.Iif(isMergePull, CommentTypeMergePull, CommentTypeClose))
+}
+
+// ErrIssueIsOpen is used when reopen an opened issue
+type ErrIssueIsOpen struct {
+ ID int64
+ RepoID int64
+ IsPull bool
+ Index int64
+}
+
+// IsErrIssueIsOpen checks if an error is a ErrIssueIsOpen.
+func IsErrIssueIsOpen(err error) bool {
+ _, ok := err.(ErrIssueIsOpen)
+ return ok
+}
+
+func (err ErrIssueIsOpen) Error() string {
+ return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already open", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
+}
+
+func setIssueAsReopen(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
+ if !issue.IsClosed {
+ return nil, ErrIssueIsOpen{
+ ID: issue.ID,
+ RepoID: issue.RepoID,
+ Index: issue.Index,
+ IsPull: issue.IsPull,
+ }
+ }
+
+ issue.IsClosed = false
+ issue.ClosedUnix = 0
+
+ if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
+ Where("is_closed = ?", true).
+ Update(issue); err != nil {
+ return nil, err
+ } else if cnt != 1 {
+ return nil, ErrIssueAlreadyChanged
+ }
+
+ return updateIssueNumbers(ctx, issue, doer, CommentTypeReopen)
+}
+
+func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User, cmtType CommentType) (*Comment, error) {
// Update issue count of labels
if err := issue.LoadLabels(ctx); err != nil {
return nil, err
@@ -103,14 +150,6 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
return nil, err
}
- // New action comment
- cmtType := CommentTypeClose
- if !issue.IsClosed {
- cmtType = CommentTypeReopen
- } else if isMergePull {
- cmtType = CommentTypeMergePull
- }
-
return CreateComment(ctx, &CreateCommentOptions{
Type: cmtType,
Doer: doer,
@@ -119,8 +158,8 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
})
}
-// ChangeIssueStatus changes issue status to open or closed.
-func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) {
+// CloseIssue changes issue status to closed.
+func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
if err := issue.LoadRepo(ctx); err != nil {
return nil, err
}
@@ -128,7 +167,45 @@ func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User,
return nil, err
}
- return changeIssueStatus(ctx, issue, doer, isClosed, false)
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer committer.Close()
+
+ comment, err := SetIssueAsClosed(ctx, issue, doer, false)
+ if err != nil {
+ return nil, err
+ }
+ if err := committer.Commit(); err != nil {
+ return nil, err
+ }
+ return comment, nil
+}
+
+// ReopenIssue changes issue status to open.
+func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
+ if err := issue.LoadRepo(ctx); err != nil {
+ return nil, err
+ }
+ if err := issue.LoadPoster(ctx); err != nil {
+ return nil, err
+ }
+
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer committer.Close()
+
+ comment, err := setIssueAsReopen(ctx, issue, doer)
+ if err != nil {
+ return nil, err
+ }
+ if err := committer.Commit(); err != nil {
+ return nil, err
+ }
+ return comment, nil
}
// ChangeIssueTitle changes the title of this issue, as the given user.
@@ -139,7 +216,7 @@ func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User,
}
defer committer.Close()
- issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
+ issue.Title = util.EllipsisDisplayString(issue.Title, 255)
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
return fmt.Errorf("updateIssueCols: %w", err)
}
@@ -227,7 +304,7 @@ func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string)
if err != nil {
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
- for i := 0; i < len(attachments); i++ {
+ for i := range attachments {
attachments[i].IssueID = issueID
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
@@ -307,10 +384,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
}
if opts.Issue.Index <= 0 {
- return fmt.Errorf("no issue index provided")
+ return errors.New("no issue index provided")
}
if opts.Issue.ID > 0 {
- return fmt.Errorf("issue exist")
+ return errors.New("issue exist")
}
if _, err := e.Insert(opts.Issue); err != nil {
@@ -367,19 +444,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
return err
}
- if len(opts.Attachments) > 0 {
- attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, opts.Attachments)
- if err != nil {
- return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", opts.Attachments, err)
- }
-
- for i := 0; i < len(attachments); i++ {
- attachments[i].IssueID = opts.Issue.ID
- if _, err = e.ID(attachments[i].ID).Update(attachments[i]); err != nil {
- return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
- }
- }
+ if err := UpdateIssueAttachments(ctx, opts.Issue.ID, opts.Attachments); err != nil {
+ return err
}
+
if err = opts.Issue.LoadAttributes(ctx); err != nil {
return err
}
@@ -402,7 +470,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, la
}
issue.Index = idx
- issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
+ issue.Title = util.EllipsisDisplayString(issue.Title, 255)
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
@@ -541,7 +609,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
unittype = unit.TypePullRequests
}
for _, team := range teams {
- if team.AccessMode >= perm.AccessModeAdmin {
+ if team.HasAdminAccess() {
checked = append(checked, team.ID)
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
continue
@@ -645,137 +713,13 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
return err
}
-// DeleteIssuesByRepoID deletes issues by repositories id
-func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
- // MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
- // so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
- sess := db.GetEngine(ctx)
-
- for {
- issueIDs := make([]int64, 0, db.DefaultMaxInSize)
-
- err := sess.Table(&Issue{}).Where("repo_id = ?", repoID).OrderBy("id").Limit(db.DefaultMaxInSize).Cols("id").Find(&issueIDs)
- if err != nil {
- return nil, err
- }
-
- if len(issueIDs) == 0 {
- break
- }
-
- // Delete content histories
- _, err = sess.In("issue_id", issueIDs).Delete(&ContentHistory{})
- if err != nil {
- return nil, err
- }
-
- // Delete comments and attachments
- _, err = sess.In("issue_id", issueIDs).Delete(&Comment{})
- if err != nil {
- return nil, err
- }
-
- // Dependencies for issues in this repository
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueDependency{})
- if err != nil {
- return nil, err
- }
-
- // Delete dependencies for issues in other repositories
- _, err = sess.In("dependency_id", issueIDs).Delete(&IssueDependency{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&TrackedTime{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&project_model.ProjectIssue{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("dependent_issue_id", issueIDs).Delete(&Comment{})
- if err != nil {
- return nil, err
- }
-
- var attachments []*repo_model.Attachment
- err = sess.In("issue_id", issueIDs).Find(&attachments)
- if err != nil {
- return nil, err
- }
-
- for j := range attachments {
- attachmentPaths = append(attachmentPaths, attachments[j].RelativePath())
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&repo_model.Attachment{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("id", issueIDs).Delete(&Issue{})
- if err != nil {
- return nil, err
- }
+func GetOrphanedIssueRepoIDs(ctx context.Context) ([]int64, error) {
+ var repoIDs []int64
+ if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
+ Join("LEFT", "repository", "issue.repo_id=repository.id").
+ Where(builder.IsNull{"repository.id"}).
+ Find(&repoIDs); err != nil {
+ return nil, err
}
-
- return attachmentPaths, err
-}
-
-// DeleteOrphanedIssues delete issues without a repo
-func DeleteOrphanedIssues(ctx context.Context) error {
- var attachmentPaths []string
- err := db.WithTx(ctx, func(ctx context.Context) error {
- var ids []int64
-
- if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
- Join("LEFT", "repository", "issue.repo_id=repository.id").
- Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
- Find(&ids); err != nil {
- return err
- }
-
- for i := range ids {
- paths, err := DeleteIssuesByRepoID(ctx, ids[i])
- if err != nil {
- return err
- }
- attachmentPaths = append(attachmentPaths, paths...)
- }
-
- return nil
- })
- if err != nil {
- return err
- }
-
- // Remove issue attachment files.
- for i := range attachmentPaths {
- system_model.RemoveAllWithNotice(ctx, "Delete issue attachment", attachmentPaths[i])
- }
- return nil
+ return repoIDs, nil
}
diff --git a/models/issues/issue_user_test.go b/models/issues/issue_user_test.go
index ce47adb53a..7c21aa15ee 100644
--- a/models/issues/issue_user_test.go
+++ b/models/issues/issue_user_test.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func Test_NewIssueUsers(t *testing.T) {
@@ -27,9 +28,8 @@ func Test_NewIssueUsers(t *testing.T) {
}
// artificially insert new issue
- unittest.AssertSuccessfulInsert(t, newIssue)
-
- assert.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
+ require.NoError(t, db.Insert(db.DefaultContext, newIssue))
+ require.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
// issue_user table should now have entries for new issue
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})
diff --git a/models/issues/issue_xref_test.go b/models/issues/issue_xref_test.go
index f1b1bb2a6b..7f257330b7 100644
--- a/models/issues/issue_xref_test.go
+++ b/models/issues/issue_xref_test.go
@@ -98,7 +98,7 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
i1 := testCreateIssue(t, 1, 2, "title1", "content1", false)
i2 := testCreateIssue(t, 1, 2, "title2", "content2", false)
i3 := testCreateIssue(t, 1, 2, "title3", "content3", false)
- _, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true)
+ _, err := issues_model.CloseIssue(db.DefaultContext, i3, d)
assert.NoError(t, err)
pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index))
diff --git a/models/issues/label.go b/models/issues/label.go
index d80578193e..cfbe100926 100644
--- a/models/issues/label.go
+++ b/models/issues/label.go
@@ -87,6 +87,7 @@ type Label struct {
OrgID int64 `xorm:"INDEX"`
Name string
Exclusive bool
+ ExclusiveOrder int `xorm:"DEFAULT 0"` // 0 means no exclusive order
Description string
Color string `xorm:"VARCHAR(7)"`
NumIssues int
@@ -236,7 +237,7 @@ func UpdateLabel(ctx context.Context, l *Label) error {
}
l.Color = color
- return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "archived_unix")
+ return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "exclusive_order", "archived_unix")
}
// DeleteLabel delete a label
@@ -299,6 +300,9 @@ func GetLabelByID(ctx context.Context, labelID int64) (*Label, error) {
// GetLabelsByIDs returns a list of labels by IDs
func GetLabelsByIDs(ctx context.Context, labelIDs []int64, cols ...string) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).Table("label").
In("id", labelIDs).
Asc("name").
@@ -349,6 +353,17 @@ func GetLabelIDsInRepoByNames(ctx context.Context, repoID int64, labelNames []st
Find(&labelIDs)
}
+// GetLabelIDsInOrgByNames returns a list of labelIDs by names in a given org.
+func GetLabelIDsInOrgByNames(ctx context.Context, orgID int64, labelNames []string) ([]int64, error) {
+ labelIDs := make([]int64, 0, len(labelNames))
+ return labelIDs, db.GetEngine(ctx).Table("label").
+ Where("org_id = ?", orgID).
+ In("name", labelNames).
+ Asc("name").
+ Cols("id").
+ Find(&labelIDs)
+}
+
// BuildLabelNamesIssueIDsCondition returns a builder where get issue ids match label names
func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
return builder.Select("issue_label.issue_id").
@@ -364,6 +379,9 @@ func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
// it silently ignores label IDs that do not belong to the repository.
func GetLabelsInRepoByIDs(ctx context.Context, repoID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).
Where("repo_id = ?", repoID).
In("id", labelIDs).
@@ -436,6 +454,9 @@ func GetLabelInOrgByID(ctx context.Context, orgID, labelID int64) (*Label, error
// it silently ignores label IDs that do not belong to the organization.
func GetLabelsInOrgByIDs(ctx context.Context, orgID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).
Where("org_id = ?", orgID).
In("id", labelIDs).
diff --git a/models/issues/label_test.go b/models/issues/label_test.go
index 1d4b6f4684..226036d543 100644
--- a/models/issues/label_test.go
+++ b/models/issues/label_test.go
@@ -20,7 +20,7 @@ func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
label.CalOpenIssues()
- assert.EqualValues(t, 2, label.NumOpenIssues)
+ assert.Equal(t, 2, label.NumOpenIssues)
}
func TestLabel_LoadSelectedLabelsAfterClick(t *testing.T) {
@@ -154,7 +154,7 @@ func TestGetLabelsByRepoID(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
- assert.EqualValues(t, expectedIssueIDs[i], label.ID)
+ assert.Equal(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(1, "leastissues", []int64{2, 1})
@@ -221,7 +221,7 @@ func TestGetLabelsByOrgID(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
- assert.EqualValues(t, expectedIssueIDs[i], label.ID)
+ assert.Equal(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(3, "leastissues", []int64{3, 4})
@@ -267,10 +267,10 @@ func TestUpdateLabel(t *testing.T) {
label.Name = update.Name
assert.NoError(t, issues_model.UpdateLabel(db.DefaultContext, update))
newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
- assert.EqualValues(t, label.ID, newLabel.ID)
- assert.EqualValues(t, label.Color, newLabel.Color)
- assert.EqualValues(t, label.Name, newLabel.Name)
- assert.EqualValues(t, label.Description, newLabel.Description)
+ assert.Equal(t, label.ID, newLabel.ID)
+ assert.Equal(t, label.Color, newLabel.Color)
+ assert.Equal(t, label.Name, newLabel.Name)
+ assert.Equal(t, label.Description, newLabel.Description)
assert.EqualValues(t, 0, newLabel.ArchivedUnix)
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}
@@ -313,7 +313,7 @@ func TestNewIssueLabel(t *testing.T) {
Content: "1",
})
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
- assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
+ assert.Equal(t, prevNumIssues+1, label.NumIssues)
// re-add existing IssueLabel
assert.NoError(t, issues_model.NewIssueLabel(db.DefaultContext, issue, label, doer))
@@ -366,11 +366,11 @@ func TestNewIssueLabels(t *testing.T) {
})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
- assert.EqualValues(t, 3, label1.NumIssues)
- assert.EqualValues(t, 1, label1.NumClosedIssues)
+ assert.Equal(t, 3, label1.NumIssues)
+ assert.Equal(t, 1, label1.NumClosedIssues)
label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
- assert.EqualValues(t, 1, label2.NumIssues)
- assert.EqualValues(t, 1, label2.NumClosedIssues)
+ assert.Equal(t, 1, label2.NumIssues)
+ assert.Equal(t, 1, label2.NumClosedIssues)
// corner case: test empty slice
assert.NoError(t, issues_model.NewIssueLabels(db.DefaultContext, issue, []*issues_model.Label{}, doer))
@@ -387,7 +387,7 @@ func TestDeleteIssueLabel(t *testing.T) {
expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues
- if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) {
+ if unittest.GetBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) != nil {
expectedNumIssues--
if issue.IsClosed {
expectedNumClosedIssues--
@@ -408,8 +408,8 @@ func TestDeleteIssueLabel(t *testing.T) {
LabelID: labelID,
}, `content=''`)
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
- assert.EqualValues(t, expectedNumIssues, label.NumIssues)
- assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
+ assert.Equal(t, expectedNumIssues, label.NumIssues)
+ assert.Equal(t, expectedNumClosedIssues, label.NumClosedIssues)
}
testSuccess(1, 1, 2)
testSuccess(2, 5, 2)
diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go
index 28cd0c028b..f73355c27d 100644
--- a/models/issues/milestone_test.go
+++ b/models/issues/milestone_test.go
@@ -69,7 +69,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
assert.Len(t, milestones, n)
for _, milestone := range milestones {
- assert.EqualValues(t, repoID, milestone.RepoID)
+ assert.Equal(t, repoID, milestone.RepoID)
}
}
test(1, api.StateOpen)
@@ -327,7 +327,7 @@ func TestUpdateMilestone(t *testing.T) {
milestone.Content = "newMilestoneContent"
assert.NoError(t, issues_model.UpdateMilestone(db.DefaultContext, milestone, milestone.IsClosed))
milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
- assert.EqualValues(t, "newMilestoneName", milestone.Name)
+ assert.Equal(t, "newMilestoneName", milestone.Name)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
@@ -364,7 +364,7 @@ func TestMigrate_InsertMilestones(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, ms)
repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
- assert.EqualValues(t, repo.NumMilestones+1, repoModified.NumMilestones)
+ assert.Equal(t, repo.NumMilestones+1, repoModified.NumMilestones)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
diff --git a/models/issues/pull.go b/models/issues/pull.go
index 853e2a69e6..0ff32e2473 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -6,10 +6,10 @@ package issues
import (
"context"
+ "errors"
"fmt"
"io"
"regexp"
- "strconv"
"strings"
"code.gitea.io/gitea/models/db"
@@ -80,22 +80,6 @@ func (err ErrPullRequestAlreadyExists) Unwrap() error {
return util.ErrAlreadyExist
}
-// ErrPullWasClosed is used close a closed pull request
-type ErrPullWasClosed struct {
- ID int64
- Index int64
-}
-
-// IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed.
-func IsErrPullWasClosed(err error) bool {
- _, ok := err.(ErrPullWasClosed)
- return ok
-}
-
-func (err ErrPullWasClosed) Error() string {
- return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index)
-}
-
// PullRequestType defines pull request type
type PullRequestType int
@@ -119,27 +103,6 @@ const (
PullRequestStatusAncestor
)
-func (status PullRequestStatus) String() string {
- switch status {
- case PullRequestStatusConflict:
- return "CONFLICT"
- case PullRequestStatusChecking:
- return "CHECKING"
- case PullRequestStatusMergeable:
- return "MERGEABLE"
- case PullRequestStatusManuallyMerged:
- return "MANUALLY_MERGED"
- case PullRequestStatusError:
- return "ERROR"
- case PullRequestStatusEmpty:
- return "EMPTY"
- case PullRequestStatusAncestor:
- return "ANCESTOR"
- default:
- return strconv.Itoa(int(status))
- }
-}
-
// PullRequestFlow the flow of pull request
type PullRequestFlow int
@@ -301,7 +264,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
return nil
}
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
@@ -320,7 +283,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
// LoadRequestedReviewersTeams loads the requested reviewers teams.
func (pr *PullRequest) LoadRequestedReviewersTeams(ctx context.Context) error {
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
@@ -499,65 +462,6 @@ func (pr *PullRequest) IsFromFork() bool {
return pr.HeadRepoID != pr.BaseRepoID
}
-// SetMerged sets a pull request to merged and closes the corresponding issue
-func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
- if pr.HasMerged {
- return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
- }
- if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
- return false, fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
- }
-
- pr.HasMerged = true
- sess := db.GetEngine(ctx)
-
- if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
- return false, err
- }
-
- if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
- return false, err
- }
-
- pr.Issue = nil
- if err := pr.LoadIssue(ctx); err != nil {
- return false, err
- }
-
- if tmpPr, err := GetPullRequestByID(ctx, pr.ID); err != nil {
- return false, err
- } else if tmpPr.HasMerged {
- if pr.Issue.IsClosed {
- return false, nil
- }
- return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
- } else if pr.Issue.IsClosed {
- return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
- }
-
- if err := pr.Issue.LoadRepo(ctx); err != nil {
- return false, err
- }
-
- if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
- return false, err
- }
-
- if _, err := changeIssueStatus(ctx, pr.Issue, pr.Merger, true, true); err != nil {
- return false, fmt.Errorf("Issue.changeStatus: %w", err)
- }
-
- // reset the conflicted files as there cannot be any if we're merged
- pr.ConflictedFiles = []string{}
-
- // We need to save all of the data used to compute this merge as it may have already been changed by TestPatch. FIXME: need to set some state to prevent TestPatch from running whilst we are merging.
- if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").Update(pr); err != nil {
- return false, fmt.Errorf("Failed to update pr[%d]: %w", pr.ID, err)
- }
-
- return true, nil
-}
-
// NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
ctx, committer, err := db.TxContext(ctx)
@@ -572,7 +476,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss
}
issue.Index = idx
- issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
+ issue.Title = util.EllipsisDisplayString(issue.Title, 255)
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
@@ -745,12 +649,6 @@ func GetAllUnmergedAgitPullRequestByPoster(ctx context.Context, uid int64) ([]*P
return pulls, err
}
-// Update updates all fields of pull request.
-func (pr *PullRequest) Update(ctx context.Context) error {
- _, err := db.GetEngine(ctx).ID(pr.ID).AllCols().Update(pr)
- return err
-}
-
// UpdateCols updates specific fields of pull request.
func (pr *PullRequest) UpdateCols(ctx context.Context, cols ...string) error {
_, err := db.GetEngine(ctx).ID(pr.ID).Cols(cols...).Update(pr)
@@ -807,7 +705,7 @@ func (pr *PullRequest) GetWorkInProgressPrefix(ctx context.Context) string {
// UpdateCommitDivergence update Divergence of a pull request
func (pr *PullRequest) UpdateCommitDivergence(ctx context.Context, ahead, behind int) error {
if pr.ID == 0 {
- return fmt.Errorf("pull ID is 0")
+ return errors.New("pull ID is 0")
}
pr.CommitsAhead = ahead
pr.CommitsBehind = behind
@@ -1000,7 +898,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
if strings.Contains(user, "/") {
s := strings.Split(user, "/")
if len(s) != 2 {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner group: %s", user))
+ warnings = append(warnings, "incorrect codeowner group: "+user)
continue
}
orgName := s[0]
@@ -1008,12 +906,12 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
org, err := org_model.GetOrgByName(ctx, orgName)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner organization: %s", user))
+ warnings = append(warnings, "incorrect codeowner organization: "+user)
continue
}
teams, err := org.LoadTeams(ctx)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner team: %s", user))
+ warnings = append(warnings, "incorrect codeowner team: "+user)
continue
}
@@ -1025,7 +923,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
} else {
u, err := user_model.GetUserByName(ctx, user)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner user: %s", user))
+ warnings = append(warnings, "incorrect codeowner user: "+user)
continue
}
rule.Users = append(rule.Users, u)
diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index 59010aa9d0..84f9f6166d 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -28,11 +28,16 @@ type PullRequestsOptions struct {
Labels []int64
MilestoneID int64
PosterID int64
+ BaseBranch string
}
func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
sess := db.GetEngine(ctx).Where("pull_request.base_repo_id=?", baseRepoID)
+ if opts.BaseBranch != "" {
+ sess.And("pull_request.base_branch=?", opts.BaseBranch)
+ }
+
sess.Join("INNER", "issue", "pull_request.issue_id = issue.id")
switch opts.State {
case "closed", "open":
@@ -56,7 +61,7 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
}
// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged
-func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
+func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) (PullRequestList, error) {
prs := make([]*PullRequest, 0, 2)
sess := db.GetEngine(ctx).
Join("INNER", "issue", "issue.id = pull_request.issue_id").
@@ -111,7 +116,7 @@ func HasUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch
// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged
// by given base information (repo and branch).
-func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
+func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) (PullRequestList, error) {
prs := make([]*PullRequest, 0, 2)
return prs, db.GetEngine(ctx).
Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
@@ -147,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
- return prs, maxResults, findSession.Find(&prs)
+ found := findSession.Find(&prs)
+ return prs, maxResults, found
}
// PullRequestList defines a list of pull requests
@@ -166,6 +172,23 @@ func (prs PullRequestList) getRepositoryIDs() []int64 {
return repoIDs.Values()
}
+func (prs PullRequestList) SetBaseRepo(baseRepo *repo_model.Repository) {
+ for _, pr := range prs {
+ if pr.BaseRepo == nil {
+ pr.BaseRepo = baseRepo
+ }
+ }
+}
+
+func (prs PullRequestList) SetHeadRepo(headRepo *repo_model.Repository) {
+ for _, pr := range prs {
+ if pr.HeadRepo == nil {
+ pr.HeadRepo = headRepo
+ pr.isHeadRepoLoaded = true
+ }
+ }
+}
+
func (prs PullRequestList) LoadRepositories(ctx context.Context) error {
repoIDs := prs.getRepositoryIDs()
reposMap := make(map[int64]*repo_model.Repository, len(repoIDs))
diff --git a/models/issues/pull_list_test.go b/models/issues/pull_list_test.go
index c7a898ca4e..eb2de006d6 100644
--- a/models/issues/pull_list_test.go
+++ b/models/issues/pull_list_test.go
@@ -16,11 +16,11 @@ import (
func TestPullRequestList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext))
+ assert.NoError(t, prs.LoadAttributes(db.DefaultContext))
for _, pr := range prs {
assert.NotNil(t, pr.Issue)
assert.Equal(t, pr.IssueID, pr.Issue.ID)
@@ -32,26 +32,26 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewComments, err := issues_model.PullRequestList(prs).LoadReviewCommentsCounts(db.DefaultContext)
+ reviewComments, err := prs.LoadReviewCommentsCounts(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, reviewComments, 2)
for _, pr := range prs {
- assert.EqualValues(t, 1, reviewComments[pr.IssueID])
+ assert.Equal(t, 1, reviewComments[pr.IssueID])
}
}
func TestPullRequestList_LoadReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewList, err := issues_model.PullRequestList(prs).LoadReviews(db.DefaultContext)
+ reviewList, err := prs.LoadReviews(db.DefaultContext)
assert.NoError(t, err)
// 1, 7, 8, 9, 10, 22
assert.Len(t, reviewList, 6)
diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go
index 090659864a..39efaa5792 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestPullRequest_LoadAttributes(t *testing.T) {
@@ -76,6 +77,47 @@ func TestPullRequestsNewest(t *testing.T) {
}
}
+func TestPullRequests_Closed_RecentSortType(t *testing.T) {
+ // Issue ID | Closed At. | Updated At
+ // 2 | 1707270001 | 1707270001
+ // 3 | 1707271000 | 1707279999
+ // 11 | 1707279999 | 1707275555
+ tests := []struct {
+ sortType string
+ expectedIssueIDOrder []int64
+ }{
+ {"recentupdate", []int64{3, 11, 2}},
+ {"recentclose", []int64{11, 3, 2}},
+ }
+
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ _, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2")
+ require.NoError(t, err)
+ _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3")
+ require.NoError(t, err)
+ _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11")
+ require.NoError(t, err)
+
+ for _, test := range tests {
+ t.Run(test.sortType, func(t *testing.T) {
+ prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{
+ ListOptions: db.ListOptions{
+ Page: 1,
+ },
+ State: "closed",
+ SortType: test.sortType,
+ })
+ require.NoError(t, err)
+
+ if assert.Len(t, prs, len(test.expectedIssueIDOrder)) {
+ for i := range test.expectedIssueIDOrder {
+ assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID)
+ }
+ }
+ })
+ }
+}
+
func TestLoadRequestedReviewers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -206,19 +248,6 @@ func TestGetPullRequestByIssueID(t *testing.T) {
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
-func TestPullRequest_Update(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
- pr.BaseBranch = "baseBranch"
- pr.HeadBranch = "headBranch"
- pr.Update(db.DefaultContext)
-
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
- assert.Equal(t, "baseBranch", pr.BaseBranch)
- assert.Equal(t, "headBranch", pr.HeadBranch)
- unittest.CheckConsistencyFor(t, pr)
-}
-
func TestPullRequest_UpdateCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := &issues_model.PullRequest{
@@ -285,7 +314,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.EqualValues(t, countBefore, countAfter)
+ assert.Equal(t, countBefore, countAfter)
}
func TestParseCodeOwnersLine(t *testing.T) {
@@ -318,7 +347,7 @@ func TestGetApprovers(t *testing.T) {
setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly = false
approvers := pr.GetApprovers(db.DefaultContext)
expected := "Reviewed-by: User Five \nReviewed-by: Org Six \n"
- assert.EqualValues(t, expected, approvers)
+ assert.Equal(t, expected, approvers)
}
func TestGetPullRequestByMergedCommit(t *testing.T) {
diff --git a/models/issues/reaction.go b/models/issues/reaction.go
index 11b3c6be20..f24001fd23 100644
--- a/models/issues/reaction.go
+++ b/models/issues/reaction.go
@@ -7,6 +7,7 @@ import (
"bytes"
"context"
"fmt"
+ "strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -321,6 +322,11 @@ func valuesUser(m map[int64]*user_model.User) []*user_model.User {
return values
}
+// newMigrationOriginalUser creates and returns a fake user for external user
+func newMigrationOriginalUser(name string) *user_model.User {
+ return &user_model.User{ID: 0, Name: name, LowerName: strings.ToLower(name)}
+}
+
// LoadUsers loads reactions' all users
func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Repository) ([]*user_model.User, error) {
if len(list) == 0 {
@@ -338,7 +344,7 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
- reaction.User = user_model.NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
+ reaction.User = newMigrationOriginalUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
} else if user, ok := userMaps[reaction.UserID]; ok {
reaction.User = user
} else {
diff --git a/models/issues/review.go b/models/issues/review.go
index 8b345e5fd8..71fdb7456f 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -5,6 +5,7 @@ package issues
import (
"context"
+ "errors"
"fmt"
"slices"
"strings"
@@ -374,7 +375,7 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
- return nil, fmt.Errorf("provide either reviewer or reviewer team")
+ return nil, errors.New("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil {
@@ -639,6 +640,10 @@ func InsertReviews(ctx context.Context, reviews []*Review) error {
return err
}
}
+
+ if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
+ return err
+ }
}
return committer.Commit()
@@ -659,7 +664,7 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
}
if review != nil {
- // skip it when reviewer hase been request to review
+ // skip it when reviewer has been request to review
if review.Type == ReviewTypeRequest {
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
}
@@ -926,17 +931,19 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us
}
// CanMarkConversation Add or remove Conversation mark for a code comment permission check
-// the PR writer , offfcial reviewer and poster can do it
+// the PR writer , official reviewer and poster can do it
func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) {
if doer == nil || issue == nil {
- return false, fmt.Errorf("issue or doer is nil")
+ return false, errors.New("issue or doer is nil")
}
+ if err = issue.LoadRepo(ctx); err != nil {
+ return false, err
+ }
+ if issue.Repo.IsArchived {
+ return false, nil
+ }
if doer.ID != issue.PosterID {
- if err = issue.LoadRepo(ctx); err != nil {
- return false, err
- }
-
p, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
if err != nil {
return false, err
@@ -966,11 +973,11 @@ func DeleteReview(ctx context.Context, r *Review) error {
defer committer.Close()
if r.ID == 0 {
- return fmt.Errorf("review is not allowed to be 0")
+ return errors.New("review is not allowed to be 0")
}
if r.Type == ReviewTypeRequest {
- return fmt.Errorf("review request can not be deleted using this method")
+ return errors.New("review request can not be deleted using this method")
}
opts := FindCommentsOptions{
diff --git a/models/issues/review_list.go b/models/issues/review_list.go
index bc7d7ec0f0..bbb8c489fa 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -5,6 +5,8 @@ package issues
import (
"context"
+ "slices"
+ "sort"
"code.gitea.io/gitea/models/db"
organization_model "code.gitea.io/gitea/models/organization"
@@ -20,7 +22,7 @@ type ReviewList []*Review
// LoadReviewers loads reviewers
func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
reviewerIDs := make([]int64, len(reviews))
- for i := 0; i < len(reviews); i++ {
+ for i := range reviews {
reviewerIDs[i] = reviews[i].ReviewerID
}
reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs)
@@ -153,43 +155,60 @@ func CountReviews(ctx context.Context, opts FindReviewOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(&Review{})
}
-// GetReviewersFromOriginalAuthorsByIssueID gets the latest review of each original authors for a pull request
-func GetReviewersFromOriginalAuthorsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
- reviews := make([]*Review, 0, 10)
-
- // Get latest review of each reviewer, sorted in order they were made
- if err := db.GetEngine(ctx).SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND original_author_id <> 0 GROUP BY issue_id, original_author_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest).
- Find(&reviews); err != nil {
- return nil, err
- }
-
- return reviews, nil
-}
-
// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
-func GetReviewsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
+// The first returned parameter is the latest review of each individual reviewer or team
+// The second returned parameter is the latest review of each original author which is migrated from other systems
+// The reviews are sorted by updated time
+func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, migratedOriginalReviews ReviewList, err error) {
reviews := make([]*Review, 0, 10)
- sess := db.GetEngine(ctx)
-
- // Get latest review of each reviewer, sorted in order they were made
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND dismissed = ? AND original_author_id = 0 GROUP BY issue_id, reviewer_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, false).
- Find(&reviews); err != nil {
- return nil, err
+ // Get all reviews for the issue id
+ if err := db.GetEngine(ctx).Where("issue_id=?", issueID).OrderBy("updated_unix ASC").Find(&reviews); err != nil {
+ return nil, nil, err
}
+ // filter them in memory to get the latest review of each reviewer
+ // Since the reviews should not be too many for one issue, less than 100 commonly, it's acceptable to do this in memory
+ // And since there are too less indexes in review table, it will be very slow to filter in the database
+ reviewersMap := make(map[int64][]*Review) // key is reviewer id
+ originalReviewersMap := make(map[int64][]*Review) // key is original author id
+ reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
+ countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
+ for _, review := range reviews {
+ if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
+ if review.OriginalAuthorID != 0 {
+ originalReviewersMap[review.OriginalAuthorID] = append(originalReviewersMap[review.OriginalAuthorID], review)
+ } else {
+ reviewersMap[review.ReviewerID] = append(reviewersMap[review.ReviewerID], review)
+ }
+ } else if review.ReviewerTeamID != 0 && review.OriginalAuthorID == 0 {
+ reviewTeamsMap[review.ReviewerTeamID] = append(reviewTeamsMap[review.ReviewerTeamID], review)
+ }
+ }
+
+ individualReviews := make([]*Review, 0, 10)
+ for _, reviews := range reviewersMap {
+ individualReviews = append(individualReviews, reviews[len(reviews)-1])
+ }
+ sort.Slice(individualReviews, func(i, j int) bool {
+ return individualReviews[i].UpdatedUnix < individualReviews[j].UpdatedUnix
+ })
+
+ originalReviews := make([]*Review, 0, 10)
+ for _, reviews := range originalReviewersMap {
+ originalReviews = append(originalReviews, reviews[len(reviews)-1])
+ }
+ sort.Slice(originalReviews, func(i, j int) bool {
+ return originalReviews[i].UpdatedUnix < originalReviews[j].UpdatedUnix
+ })
+
teamReviewRequests := make([]*Review, 0, 5)
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id <> 0 AND original_author_id = 0 GROUP BY issue_id, reviewer_team_id) ORDER BY review.updated_unix ASC",
- issueID).
- Find(&teamReviewRequests); err != nil {
- return nil, err
+ for _, reviews := range reviewTeamsMap {
+ teamReviewRequests = append(teamReviewRequests, reviews[len(reviews)-1])
}
+ sort.Slice(teamReviewRequests, func(i, j int) bool {
+ return teamReviewRequests[i].UpdatedUnix < teamReviewRequests[j].UpdatedUnix
+ })
- if len(teamReviewRequests) > 0 {
- reviews = append(reviews, teamReviewRequests...)
- }
-
- return reviews, nil
+ return append(individualReviews, teamReviewRequests...), originalReviews, nil
}
diff --git a/models/issues/review_test.go b/models/issues/review_test.go
index 50330e3ff2..2588b8ba41 100644
--- a/models/issues/review_test.go
+++ b/models/issues/review_test.go
@@ -162,8 +162,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
},
)
- allReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
+ allReviews, migratedReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
assert.NoError(t, err)
+ assert.Empty(t, migratedReviews)
for _, review := range allReviews {
assert.NoError(t, review.LoadReviewer(db.DefaultContext))
}
diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go
index 629af95b57..761b8f91a0 100644
--- a/models/issues/stopwatch.go
+++ b/models/issues/stopwatch.go
@@ -5,7 +5,6 @@ package issues
import (
"context"
- "fmt"
"time"
"code.gitea.io/gitea/models/db"
@@ -15,20 +14,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
-// ErrIssueStopwatchNotExist represents an error that stopwatch is not exist
-type ErrIssueStopwatchNotExist struct {
- UserID int64
- IssueID int64
-}
-
-func (err ErrIssueStopwatchNotExist) Error() string {
- return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
-}
-
-func (err ErrIssueStopwatchNotExist) Unwrap() error {
- return util.ErrNotExist
-}
-
// Stopwatch represents a stopwatch for time tracking.
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
@@ -46,11 +31,6 @@ func (s Stopwatch) Seconds() int64 {
return int64(timeutil.TimeStampNow() - s.CreatedUnix)
}
-// Duration returns a human-readable duration string based on local server time
-func (s Stopwatch) Duration() string {
- return util.SecToTime(s.Seconds())
-}
-
func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
sw = new(Stopwatch)
exists, err = db.GetEngine(ctx).
@@ -60,13 +40,11 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex
return sw, exists, err
}
-// UserIDCount is a simple coalition of UserID and Count
type UserStopwatch struct {
UserID int64
StopWatches []*Stopwatch
}
-// GetUIDsAndNotificationCounts between the two provided times
func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
sws := []*Stopwatch{}
if err := db.GetEngine(ctx).Where("issue_id != 0").Find(&sws); err != nil {
@@ -92,7 +70,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
return res, nil
}
-// GetUserStopwatches return list of all stopwatches of a user
+// GetUserStopwatches return list of the user's all stopwatches
func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
sws := make([]*Stopwatch, 0, 8)
sess := db.GetEngine(ctx).Where("stopwatch.user_id = ?", userID)
@@ -107,7 +85,7 @@ func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOp
return sws, nil
}
-// CountUserStopwatches return count of all stopwatches of a user
+// CountUserStopwatches return count of the user's all stopwatches
func CountUserStopwatches(ctx context.Context, userID int64) (int64, error) {
return db.GetEngine(ctx).Where("user_id = ?", userID).Count(&Stopwatch{})
}
@@ -141,43 +119,21 @@ func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopw
return exists, sw, issue, err
}
-// FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore
-func FinishIssueStopwatchIfPossible(ctx context.Context, user *user_model.User, issue *Issue) error {
- _, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
- if !exists {
- return nil
- }
- return FinishIssueStopwatch(ctx, user, issue)
-}
-
-// CreateOrStopIssueStopwatch create an issue stopwatch if it's not exist, otherwise finish it
-func CreateOrStopIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- _, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
- if exists {
- return FinishIssueStopwatch(ctx, user, issue)
- }
- return CreateIssueStopwatch(ctx, user, issue)
-}
-
-// FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error
-func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
+// FinishIssueStopwatch if stopwatch exists, then finish it.
+func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
if err != nil {
- return err
+ return false, err
+ } else if !exists {
+ return false, nil
}
- if !exists {
- return ErrIssueStopwatchNotExist{
- UserID: user.ID,
- IssueID: issue.ID,
- }
+ if err = finishIssueStopwatch(ctx, user, issue, sw); err != nil {
+ return false, err
}
+ return true, nil
+}
+func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue, sw *Stopwatch) error {
// Create tracked time out of the time difference between start date and actual date
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
@@ -189,101 +145,81 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
Time: timediff,
}
- if err := db.Insert(ctx, tt); err != nil {
- return err
- }
-
if err := issue.LoadRepo(ctx); err != nil {
return err
}
-
+ if err := db.Insert(ctx, tt); err != nil {
+ return err
+ }
if _, err := CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
- Content: util.SecToTime(timediff),
+ Content: util.SecToHours(timediff),
Type: CommentTypeStopTracking,
TimeID: tt.ID,
}); err != nil {
return err
}
- _, err = db.DeleteByBean(ctx, sw)
+ _, err := db.DeleteByBean(ctx, sw)
return err
}
-// CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error
-func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- if err := issue.LoadRepo(ctx); err != nil {
- return err
- }
-
- // if another stopwatch is running: stop it
- exists, _, otherIssue, err := HasUserStopwatch(ctx, user.ID)
- if err != nil {
- return err
- }
- if exists {
- if err := FinishIssueStopwatch(ctx, user, otherIssue); err != nil {
- return err
+// CreateIssueStopwatch creates a stopwatch if the issue doesn't have the user's stopwatch.
+// It also stops any other stopwatch that might be running for the user.
+func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
+ { // if another issue's stopwatch is running: stop it; if this issue has a stopwatch: return an error.
+ exists, otherStopWatch, otherIssue, err := HasUserStopwatch(ctx, user.ID)
+ if err != nil {
+ return false, err
+ }
+ if exists {
+ if otherStopWatch.IssueID == issue.ID {
+ // don't allow starting stopwatch for the same issue
+ return false, nil
+ }
+ // stop the other issue's stopwatch
+ if err = finishIssueStopwatch(ctx, user, otherIssue, otherStopWatch); err != nil {
+ return false, err
+ }
}
}
- // Create stopwatch
- sw := &Stopwatch{
- UserID: user.ID,
- IssueID: issue.ID,
+ if err = issue.LoadRepo(ctx); err != nil {
+ return false, err
}
-
- if err := db.Insert(ctx, sw); err != nil {
- return err
+ if err = db.Insert(ctx, &Stopwatch{UserID: user.ID, IssueID: issue.ID}); err != nil {
+ return false, err
}
-
- if err := issue.LoadRepo(ctx); err != nil {
- return err
- }
-
- if _, err := CreateComment(ctx, &CreateCommentOptions{
+ if _, err = CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
Type: CommentTypeStartTracking,
}); err != nil {
- return err
+ return false, err
}
-
- return nil
+ return true, nil
}
// CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
-func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- if err := cancelStopwatch(ctx, user, issue); err != nil {
- return err
- }
- return committer.Commit()
-}
-
-func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- e := db.GetEngine(ctx)
- sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
-
- if exists {
- if _, err := e.Delete(sw); err != nil {
+func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
+ err = db.WithTx(ctx, func(ctx context.Context) error {
+ e := db.GetEngine(ctx)
+ sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
+ if err != nil {
return err
+ } else if !exists {
+ return nil
}
- if err := issue.LoadRepo(ctx); err != nil {
+ if err = issue.LoadRepo(ctx); err != nil {
return err
}
-
- if _, err := CreateComment(ctx, &CreateCommentOptions{
+ if _, err = e.Delete(sw); err != nil {
+ return err
+ }
+ if _, err = CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
@@ -291,6 +227,8 @@ func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) e
}); err != nil {
return err
}
- }
- return nil
+ ok = true
+ return nil
+ })
+ return ok, err
}
diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go
index a1bf9dc931..6333c10234 100644
--- a/models/issues/stopwatch_test.go
+++ b/models/issues/stopwatch_test.go
@@ -10,7 +10,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
@@ -18,26 +17,22 @@ import (
func TestCancelStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1, err := user_model.GetUserByID(db.DefaultContext, 1)
- assert.NoError(t, err)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
- assert.NoError(t, err)
- issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
- assert.NoError(t, err)
-
- err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
+ ok, err := issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
assert.NoError(t, err)
+ assert.True(t, ok)
unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user1.ID, IssueID: issue1.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
- _ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
-
- assert.NoError(t, issues_model.CancelStopwatch(db.DefaultContext, user1, issue2))
+ ok, err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
+ assert.NoError(t, err)
+ assert.False(t, ok)
}
func TestStopwatchExists(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
-
assert.True(t, issues_model.StopwatchExists(db.DefaultContext, 1, 1))
assert.False(t, issues_model.StopwatchExists(db.DefaultContext, 1, 2))
}
@@ -58,21 +53,35 @@ func TestHasUserStopwatch(t *testing.T) {
func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user2, err := user_model.GetUserByID(db.DefaultContext, 2)
- assert.NoError(t, err)
- org3, err := user_model.GetUserByID(db.DefaultContext, 3)
- assert.NoError(t, err)
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
- issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
+ // create a new stopwatch
+ ok, err := issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1)
assert.NoError(t, err)
- issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
+ assert.True(t, ok)
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID})
+ // should not create a second stopwatch for the same issue
+ ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1)
assert.NoError(t, err)
+ assert.False(t, ok)
+ // on a different issue, it will finish the existing stopwatch and create a new one
+ ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue3)
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue3.ID})
- assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, org3, issue1))
- sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1})
- assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow())
-
- assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, user2, issue2))
- unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2})
- unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2})
+ // user2 already has a stopwatch in test fixture
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2)
+ assert.NoError(t, err)
+ assert.True(t, ok)
+ unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user2.ID, IssueID: issue2.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: user2.ID, IssueID: issue2.ID})
+ ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2)
+ assert.NoError(t, err)
+ assert.False(t, ok)
}
diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go
index ea404d36cd..2afbe272ed 100644
--- a/models/issues/tracked_time.go
+++ b/models/issues/tracked_time.go
@@ -350,10 +350,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed
// we get the statistics in smaller chunks and get accumulates
var accum int64
for i := 0; i < len(opts.IssueIDs); {
- chunk := i + MaxQueryParameters
- if chunk > len(opts.IssueIDs) {
- chunk = len(opts.IssueIDs)
- }
+ chunk := min(i+MaxQueryParameters, len(opts.IssueIDs))
time, err := getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs[i:chunk])
if err != nil {
return 0, err
diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go
index eb1c44a79e..479a46379c 100644
--- a/models/migrations/base/db.go
+++ b/models/migrations/base/db.go
@@ -52,7 +52,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
// TODO: This will not work if there are foreign keys
tableName := sess.Engine().TableName(bean)
- tempTableName := fmt.Sprintf("tmp_recreate__%s", tableName)
+ tempTableName := "tmp_recreate__" + tableName
// We need to move the old table away and create a new one with the correct columns
// We will need to do this in stages to prevent data loss
@@ -82,7 +82,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
}
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
- return fmt.Errorf("no columns in new table")
+ return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {
@@ -518,7 +518,7 @@ func ModifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error {
func removeAllWithRetry(dir string) error {
var err error
- for i := 0; i < 20; i++ {
+ for range 20 {
err = os.RemoveAll(dir)
if err == nil {
break
@@ -552,11 +552,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
return err
}
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
return err
}
return nil
@@ -568,11 +568,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
return err
}
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
return err
}
db.Close()
@@ -594,7 +594,7 @@ func deleteDB() error {
if !schrows.Next() {
// Create and setup a DB schema
- _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
+ _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema)
if err != nil {
return err
}
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index c2134f702a..33fd1df707 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -1,7 +1,6 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
package base
import (
@@ -13,9 +12,10 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/tempdir"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/testlogger"
"github.com/stretchr/testify/require"
@@ -76,7 +76,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
return x, deferFn
}
- if err := unittest.LoadFixtures(x); err != nil {
+ if err := unittest.LoadFixtures(); err != nil {
t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
return x, deferFn
}
@@ -92,10 +92,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
func MainTest(m *testing.M) {
testlogger.Init()
- giteaRoot := base.SetupGiteaRoot()
- if giteaRoot == "" {
- testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
- }
+ giteaRoot := test.SetupGiteaRoot()
giteaBinary := "gitea"
if runtime.GOOS == "windows" {
giteaBinary += ".exe"
@@ -108,7 +105,7 @@ func MainTest(m *testing.M) {
giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" {
giteaConf = filepath.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
- fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
+ _, _ = fmt.Fprintf(os.Stderr, "Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
}
if !filepath.IsAbs(giteaConf) {
@@ -117,15 +114,16 @@ func MainTest(m *testing.M) {
setting.CustomConf = giteaConf
}
- tmpDataPath, err := os.MkdirTemp("", "data")
+ tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
if err != nil {
testlogger.Fatalf("Unable to create temporary data path %v\n", err)
}
+ defer cleanup()
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
setting.AppDataPath = tmpDataPath
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
if err = git.InitFull(context.Background()); err != nil {
testlogger.Fatalf("Unable to InitFull: %v\n", err)
}
@@ -135,10 +133,7 @@ func MainTest(m *testing.M) {
exitStatus := m.Run()
if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
- fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
- }
- if err := removeAllWithRetry(tmpDataPath); err != nil {
- fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
}
os.Exit(exitStatus)
}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 52d10c4fe8..176372486e 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -6,6 +6,7 @@ package migrations
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/v1_10"
@@ -22,6 +23,7 @@ import (
"code.gitea.io/gitea/models/migrations/v1_21"
"code.gitea.io/gitea/models/migrations/v1_22"
"code.gitea.io/gitea/models/migrations/v1_23"
+ "code.gitea.io/gitea/models/migrations/v1_24"
"code.gitea.io/gitea/models/migrations/v1_6"
"code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8"
@@ -369,6 +371,17 @@ func prepareMigrationTasks() []*migration {
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
+
+ // Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
+ newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
+ newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
+ newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
+ newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
+ newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
+ newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard),
+ newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
+ newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
+ newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
}
return preparedMigrations
}
@@ -407,14 +420,14 @@ func ExpectedDBVersion() int64 {
}
// EnsureUpToDate will check if the db is at the correct version
-func EnsureUpToDate(x *xorm.Engine) error {
+func EnsureUpToDate(ctx context.Context, x *xorm.Engine) error {
currentDB, err := GetCurrentDBVersion(x)
if err != nil {
return err
}
if currentDB < 0 {
- return fmt.Errorf("database has not been initialized")
+ return errors.New("database has not been initialized")
}
if minDBVersion > currentDB {
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index e66b015b3d..8649d116f5 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -22,7 +22,7 @@ func TestMigrations(t *testing.T) {
assert.EqualValues(t, 71, migrationIDNumberToDBVersion(70))
- assert.EqualValues(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
- assert.EqualValues(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
- assert.EqualValues(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
+ assert.Equal(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
+ assert.Equal(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
+ assert.Equal(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
}
diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go
index 5d2fd8e244..1742bea296 100644
--- a/models/migrations/v1_10/v100.go
+++ b/models/migrations/v1_10/v100.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"net/url"
diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go
index f023a2a0e7..6c8dfe2486 100644
--- a/models/migrations/v1_10/v101.go
+++ b/models/migrations/v1_10/v101.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go
index 7e86ac364f..eb8e81c19e 100644
--- a/models/migrations/v1_10/v88.go
+++ b/models/migrations/v1_10/v88.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"crypto/sha1"
diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go
index d5f27ffdc6..0df2a6e17b 100644
--- a/models/migrations/v1_10/v89.go
+++ b/models/migrations/v1_10/v89.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go
index 295d4b1c1b..5521a97e32 100644
--- a/models/migrations/v1_10/v90.go
+++ b/models/migrations/v1_10/v90.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go
index 48cac2de70..08db6c2742 100644
--- a/models/migrations/v1_10/v91.go
+++ b/models/migrations/v1_10/v91.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go
index 9080108594..b6c04a9234 100644
--- a/models/migrations/v1_10/v92.go
+++ b/models/migrations/v1_10/v92.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go
index ee59a8db39..c131be9a8d 100644
--- a/models/migrations/v1_10/v93.go
+++ b/models/migrations/v1_10/v93.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go
index c131af162b..13b7d7b303 100644
--- a/models/migrations/v1_10/v94.go
+++ b/models/migrations/v1_10/v94.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go
index 3b1f67fd9c..86b52026bf 100644
--- a/models/migrations/v1_10/v95.go
+++ b/models/migrations/v1_10/v95.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go
index 34c8240031..ca35a169c4 100644
--- a/models/migrations/v1_10/v96.go
+++ b/models/migrations/v1_10/v96.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"path/filepath"
diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go
index dee45b32e3..5872bb63e5 100644
--- a/models/migrations/v1_10/v97.go
+++ b/models/migrations/v1_10/v97.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go
index bdd9aed089..d21c326459 100644
--- a/models/migrations/v1_10/v98.go
+++ b/models/migrations/v1_10/v98.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go
index ebe6597f7c..223c188057 100644
--- a/models/migrations/v1_10/v99.go
+++ b/models/migrations/v1_10/v99.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go
index 9358e4cef3..e52290afb0 100644
--- a/models/migrations/v1_11/v102.go
+++ b/models/migrations/v1_11/v102.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go
index 53527dac58..a515710160 100644
--- a/models/migrations/v1_11/v103.go
+++ b/models/migrations/v1_11/v103.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go
index 3e8ee64bc1..3b0d3c64b2 100644
--- a/models/migrations/v1_11/v104.go
+++ b/models/migrations/v1_11/v104.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go
index b91340c30a..d86973a0f6 100644
--- a/models/migrations/v1_11/v105.go
+++ b/models/migrations/v1_11/v105.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go
index ecb11cdd1e..edffe18683 100644
--- a/models/migrations/v1_11/v106.go
+++ b/models/migrations/v1_11/v106.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go
index f0bfe5862c..a158e3bb50 100644
--- a/models/migrations/v1_11/v107.go
+++ b/models/migrations/v1_11/v107.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go
index a85096234d..8f14504ceb 100644
--- a/models/migrations/v1_11/v108.go
+++ b/models/migrations/v1_11/v108.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go
index ea565ccda3..f7616aec7b 100644
--- a/models/migrations/v1_11/v109.go
+++ b/models/migrations/v1_11/v109.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go
index 81afa1331d..512f728c03 100644
--- a/models/migrations/v1_11/v110.go
+++ b/models/migrations/v1_11/v110.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go
index ff108479a9..c27465f051 100644
--- a/models/migrations/v1_11/v111.go
+++ b/models/migrations/v1_11/v111.go
@@ -1,10 +1,11 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"fmt"
+ "slices"
"xorm.io/xorm"
)
@@ -344,10 +345,8 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
}
return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil
}
- for _, id := range protectedBranch.ApprovalsWhitelistUserIDs {
- if id == reviewer.ID {
- return true, nil
- }
+ if slices.Contains(protectedBranch.ApprovalsWhitelistUserIDs, reviewer.ID) {
+ return true, nil
}
// isUserInTeams
diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go
index 0857663119..fe45cf9222 100644
--- a/models/migrations/v1_11/v112.go
+++ b/models/migrations/v1_11/v112.go
@@ -1,12 +1,12 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
- "fmt"
"path/filepath"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -31,7 +31,7 @@ func RemoveAttachmentMissedRepo(x *xorm.Engine) error {
for i := 0; i < len(attachments); i++ {
uuid := attachments[i].UUID
if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
- fmt.Printf("Error: %v", err) //nolint:forbidigo
+ log.Warn("Unable to remove attachment file by UUID %s: %v", uuid, err)
}
}
diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go
index dea344a44f..a4d54f66fb 100644
--- a/models/migrations/v1_11/v113.go
+++ b/models/migrations/v1_11/v113.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"fmt"
diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go
index 95adcee989..9467a8a90c 100644
--- a/models/migrations/v1_11/v114.go
+++ b/models/migrations/v1_11/v114.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"net/url"
diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go
index 8c631cfd0b..5933c0520f 100644
--- a/models/migrations/v1_11/v115.go
+++ b/models/migrations/v1_11/v115.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"crypto/md5"
@@ -146,7 +146,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error)
return "", fmt.Errorf("io.ReadAll: %w", err)
}
- newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data)))))
+ newAvatar := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data))))
if newAvatar == oldAvatar {
return newAvatar, nil
}
diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go
index 85aa76c1e0..729fbad18b 100644
--- a/models/migrations/v1_11/v116.go
+++ b/models/migrations/v1_11/v116.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go
index 8eadcdef2b..73b58ca34b 100644
--- a/models/migrations/v1_12/v117.go
+++ b/models/migrations/v1_12/v117.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go
index eb022dc5e4..e8b4249743 100644
--- a/models/migrations/v1_12/v118.go
+++ b/models/migrations/v1_12/v118.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go
index 60bfe6a57d..b4bf29a935 100644
--- a/models/migrations/v1_12/v119.go
+++ b/models/migrations/v1_12/v119.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go
index 3f7ed8d373..14d515f5a7 100644
--- a/models/migrations/v1_12/v120.go
+++ b/models/migrations/v1_12/v120.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go
index 175ec9164d..a28ae4e1c9 100644
--- a/models/migrations/v1_12/v121.go
+++ b/models/migrations/v1_12/v121.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go
index 6e31d863a1..bc1b175f6a 100644
--- a/models/migrations/v1_12/v122.go
+++ b/models/migrations/v1_12/v122.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go
index b0c3af07a3..52b10bb850 100644
--- a/models/migrations/v1_12/v123.go
+++ b/models/migrations/v1_12/v123.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go
index d2ba03ffe0..9a93f436d4 100644
--- a/models/migrations/v1_12/v124.go
+++ b/models/migrations/v1_12/v124.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go
index ec4ffaab25..7f582ecff5 100644
--- a/models/migrations/v1_12/v125.go
+++ b/models/migrations/v1_12/v125.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go
index ca9ec3aa3f..64fd7f7478 100644
--- a/models/migrations/v1_12/v126.go
+++ b/models/migrations/v1_12/v126.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go
index 00e391dc87..9bd78db95e 100644
--- a/models/migrations/v1_12/v127.go
+++ b/models/migrations/v1_12/v127.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go
index 44d44a26c5..e7dbff3766 100644
--- a/models/migrations/v1_12/v128.go
+++ b/models/migrations/v1_12/v128.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
@@ -82,17 +82,17 @@ func FixMergeBase(x *xorm.Engine) error {
if !pr.HasMerged {
var err error
- pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
- pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(git.DefaultContext, &git.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(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.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
@@ -104,9 +104,9 @@ func FixMergeBase(x *xorm.Engine) error {
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
- cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand("merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.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/v129.go b/models/migrations/v1_12/v129.go
index cf228242b9..3e4d3aca68 100644
--- a/models/migrations/v1_12/v129.go
+++ b/models/migrations/v1_12/v129.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go
index 391810c7ca..107bb756fd 100644
--- a/models/migrations/v1_12/v130.go
+++ b/models/migrations/v1_12/v130.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go
index 5184bc3590..1266c2f185 100644
--- a/models/migrations/v1_12/v131.go
+++ b/models/migrations/v1_12/v131.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go
index 3b2b28f7ab..8b1ae6db93 100644
--- a/models/migrations/v1_12/v132.go
+++ b/models/migrations/v1_12/v132.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go
index c9087fc8c1..69e20597d8 100644
--- a/models/migrations/v1_12/v133.go
+++ b/models/migrations/v1_12/v133.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go
index 3d1c82f09e..09d743964d 100644
--- a/models/migrations/v1_12/v134.go
+++ b/models/migrations/v1_12/v134.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
@@ -79,7 +79,7 @@ func RefixMergeBase(x *xorm.Engine) error {
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
- parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.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
@@ -92,9 +92,9 @@ func RefixMergeBase(x *xorm.Engine) error {
// we should recalculate
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
- cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand("merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.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/v135.go b/models/migrations/v1_12/v135.go
index 8898011df5..5df0ad7fc4 100644
--- a/models/migrations/v1_12/v135.go
+++ b/models/migrations/v1_12/v135.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go
index d91ff92feb..0f53278b46 100644
--- a/models/migrations/v1_12/v136.go
+++ b/models/migrations/v1_12/v136.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go
index 0d86b72010..9d38483488 100644
--- a/models/migrations/v1_12/v137.go
+++ b/models/migrations/v1_12/v137.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go
index 8c8d353f40..4485adeb2d 100644
--- a/models/migrations/v1_12/v138.go
+++ b/models/migrations/v1_12/v138.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go
index 279aa7df87..a3799841ac 100644
--- a/models/migrations/v1_12/v139.go
+++ b/models/migrations/v1_12/v139.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go
index 2d3337012d..a9a047bca9 100644
--- a/models/migrations/v1_13/v140.go
+++ b/models/migrations/v1_13/v140.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
@@ -21,12 +21,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
// RepoIndexerType specifies the repository indexer type
type RepoIndexerType int
- const (
- // RepoIndexerTypeCode code indexer - 0
- RepoIndexerTypeCode RepoIndexerType = iota //nolint:unused
- // RepoIndexerTypeStats repository stats indexer - 1
- RepoIndexerTypeStats
- )
+ const RepoIndexerTypeStats RepoIndexerType = 1
// RepoIndexerStatus see models/repo_indexer.go
type RepoIndexerStatus struct {
@@ -46,7 +41,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
}
// Delete language stats
- if _, err := x.Exec(fmt.Sprintf("%s language_stat", truncExpr)); err != nil {
+ if _, err := x.Exec(truncExpr + " language_stat"); err != nil {
return err
}
diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go
index ae211e0e44..b54bc1727c 100644
--- a/models/migrations/v1_13/v141.go
+++ b/models/migrations/v1_13/v141.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go
index 7c7c01ad47..d08a0ae0bf 100644
--- a/models/migrations/v1_13/v142.go
+++ b/models/migrations/v1_13/v142.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go
index 885768dff3..b9a856ed0f 100644
--- a/models/migrations/v1_13/v143.go
+++ b/models/migrations/v1_13/v143.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go
index f5a0bc5751..9352d78bc8 100644
--- a/models/migrations/v1_13/v144.go
+++ b/models/migrations/v1_13/v144.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go
index 8acb29bf33..86ebb4f9d9 100644
--- a/models/migrations/v1_13/v145.go
+++ b/models/migrations/v1_13/v145.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
@@ -42,7 +42,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
switch {
case setting.Database.Type.IsMySQL():
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat MODIFY COLUMN language %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat MODIFY COLUMN language " + sqlType); err != nil {
return err
}
case setting.Database.Type.IsMSSQL():
@@ -64,7 +64,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
return fmt.Errorf("Drop table `language_stat` constraint `%s`: %w", constraint, err)
}
}
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language " + sqlType); err != nil {
return err
}
// Finally restore the constraint
@@ -72,7 +72,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
return err
}
case setting.Database.Type.IsPostgreSQL():
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language TYPE %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language TYPE " + sqlType); err != nil {
return err
}
}
diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go
index 7d9a878704..355c772c26 100644
--- a/models/migrations/v1_13/v146.go
+++ b/models/migrations/v1_13/v146.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go
index 510ef39b28..0059c06220 100644
--- a/models/migrations/v1_13/v147.go
+++ b/models/migrations/v1_13/v147.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go
index 7bb8ab700b..d276db3d61 100644
--- a/models/migrations/v1_13/v148.go
+++ b/models/migrations/v1_13/v148.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go
index 2a1db04cbb..a96b8e5ac7 100644
--- a/models/migrations/v1_13/v149.go
+++ b/models/migrations/v1_13/v149.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go
index d5ba489566..590ea72903 100644
--- a/models/migrations/v1_13/v150.go
+++ b/models/migrations/v1_13/v150.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go
index 25af1d03ec..454929534f 100644
--- a/models/migrations/v1_13/v151.go
+++ b/models/migrations/v1_13/v151.go
@@ -1,10 +1,11 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -113,7 +114,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error {
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
- return fmt.Errorf("no columns in new table")
+ return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {
diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go
index 502c82a40d..648e26446f 100644
--- a/models/migrations/v1_13/v152.go
+++ b/models/migrations/v1_13/v152.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import "xorm.io/xorm"
diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go
index 0b2dd3eb62..e5462fc162 100644
--- a/models/migrations/v1_13/v153.go
+++ b/models/migrations/v1_13/v153.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go
index 60cc56713e..5477d1b889 100644
--- a/models/migrations/v1_13/v154.go
+++ b/models/migrations/v1_13/v154.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go
index 7a091b9b9a..978f88577c 100644
--- a/models/migrations/v1_14/main_test.go
+++ b/models/migrations/v1_14/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go
index e814f59938..505a9ae033 100644
--- a/models/migrations/v1_14/v155.go
+++ b/models/migrations/v1_14/v155.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go
index 2cf4954a15..2fa5819610 100644
--- a/models/migrations/v1_14/v156.go
+++ b/models/migrations/v1_14/v156.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go
index 7187278d29..2c5625ebbd 100644
--- a/models/migrations/v1_14/v157.go
+++ b/models/migrations/v1_14/v157.go
@@ -1,24 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
)
func FixRepoTopics(x *xorm.Engine) error {
- type Topic struct { //nolint:unused
- ID int64 `xorm:"pk autoincr"`
- Name string `xorm:"UNIQUE VARCHAR(25)"`
- RepoCount int
- }
-
- type RepoTopic struct { //nolint:unused
- RepoID int64 `xorm:"pk"`
- TopicID int64 `xorm:"pk"`
- }
-
type Repository struct {
ID int64 `xorm:"pk autoincr"`
Topics []string `xorm:"TEXT JSON"`
diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go
index 1094d8abf7..3c57e8e3da 100644
--- a/models/migrations/v1_14/v158.go
+++ b/models/migrations/v1_14/v158.go
@@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
- "fmt"
+ "errors"
"strconv"
"code.gitea.io/gitea/modules/log"
@@ -82,7 +82,7 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error {
sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " +
"(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id"
default:
- return fmt.Errorf("Unsupported database type")
+ return errors.New("Unsupported database type")
}
if err := sess.SQL(sqlCmd).Find(&comments); err != nil {
diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go
index 149ae0f6a8..e6f6f0f061 100644
--- a/models/migrations/v1_14/v159.go
+++ b/models/migrations/v1_14/v159.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go
index 4dea91b514..73f3798954 100644
--- a/models/migrations/v1_14/v160.go
+++ b/models/migrations/v1_14/v160.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go
index ac7e821a80..eb92dee77c 100644
--- a/models/migrations/v1_14/v161.go
+++ b/models/migrations/v1_14/v161.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"context"
diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go
index 2e4e0b8eb0..a0ddd36d55 100644
--- a/models/migrations/v1_14/v162.go
+++ b/models/migrations/v1_14/v162.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go
index 0cd8ba68c8..84c35190b7 100644
--- a/models/migrations/v1_14/v163.go
+++ b/models/migrations/v1_14/v163.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go
index 54f6951427..d2fd9b8464 100644
--- a/models/migrations/v1_14/v164.go
+++ b/models/migrations/v1_14/v164.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go
index 926350cdf7..6e1b34156b 100644
--- a/models/migrations/v1_14/v165.go
+++ b/models/migrations/v1_14/v165.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
@@ -16,10 +16,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return nil
}
- type HookTask struct { //nolint:unused
- Typ string `xorm:"VARCHAR(16) index"`
- }
-
+ // HookTask: Typ string `xorm:"VARCHAR(16) index"`
if err := base.ModifyColumn(x, "hook_task", &schemas.Column{
Name: "typ",
SQLType: schemas.SQLType{
@@ -42,10 +39,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return err
}
- type Webhook struct { //nolint:unused
- Type string `xorm:"VARCHAR(16) index"`
- }
-
+ // Webhook: Type string `xorm:"VARCHAR(16) index"`
if err := base.ModifyColumn(x, "webhook", &schemas.Column{
Name: "type",
SQLType: schemas.SQLType{
diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go
index e5731582fd..4c106bd7da 100644
--- a/models/migrations/v1_14/v166.go
+++ b/models/migrations/v1_14/v166.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"crypto/sha256"
diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go
index 9d416f6a32..d77bbc401e 100644
--- a/models/migrations/v1_14/v167.go
+++ b/models/migrations/v1_14/v167.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go
index a30a8859f7..aa93eec19b 100644
--- a/models/migrations/v1_14/v168.go
+++ b/models/migrations/v1_14/v168.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import "xorm.io/xorm"
diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go
index 5b81bb58b1..4f9df0d96f 100644
--- a/models/migrations/v1_14/v169.go
+++ b/models/migrations/v1_14/v169.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go
index 7b6498a3e9..a2ff4623e1 100644
--- a/models/migrations/v1_14/v170.go
+++ b/models/migrations/v1_14/v170.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go
index 51a35a02ad..7b200e960a 100644
--- a/models/migrations/v1_14/v171.go
+++ b/models/migrations/v1_14/v171.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go
index 0f9bef902a..bbd61d87b2 100644
--- a/models/migrations/v1_14/v172.go
+++ b/models/migrations/v1_14/v172.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go
index 2d9eee9197..7752fbe966 100644
--- a/models/migrations/v1_14/v173.go
+++ b/models/migrations/v1_14/v173.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go
index c839e15db8..4049e43070 100644
--- a/models/migrations/v1_14/v174.go
+++ b/models/migrations/v1_14/v174.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go
index 70d72b2600..92ed130473 100644
--- a/models/migrations/v1_14/v175.go
+++ b/models/migrations/v1_14/v175.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go
index 1ed49f75fa..ef5dce9a02 100644
--- a/models/migrations/v1_14/v176.go
+++ b/models/migrations/v1_14/v176.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go
index ea3e750d7f..5c1db4db71 100644
--- a/models/migrations/v1_14/v176_test.go
+++ b/models/migrations/v1_14/v176_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go
index 6e1838f369..96676bf8d9 100644
--- a/models/migrations/v1_14/v177.go
+++ b/models/migrations/v1_14/v177.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go
index 5568a18fec..263f69f338 100644
--- a/models/migrations/v1_14/v177_test.go
+++ b/models/migrations/v1_14/v177_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go
index 366f19788e..d01585e997 100644
--- a/models/migrations/v1_15/main_test.go
+++ b/models/migrations/v1_15/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"testing"
diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go
index 6d236eb049..ca3a5c262e 100644
--- a/models/migrations/v1_15/v178.go
+++ b/models/migrations/v1_15/v178.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go
index f6b142eb42..d6fb86ffec 100644
--- a/models/migrations/v1_15/v179.go
+++ b/models/migrations/v1_15/v179.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go
index c71e771861..dd132f8330 100644
--- a/models/migrations/v1_15/v180.go
+++ b/models/migrations/v1_15/v180.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go
index 2185ed0213..fb1d3d7a75 100644
--- a/models/migrations/v1_15/v181.go
+++ b/models/migrations/v1_15/v181.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"strings"
diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go
index 1b075be7a0..73b5c1f3d6 100644
--- a/models/migrations/v1_15/v181_test.go
+++ b/models/migrations/v1_15/v181_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"strings"
@@ -49,7 +49,7 @@ func Test_AddPrimaryEmail2EmailAddress(t *testing.T) {
assert.NoError(t, err)
assert.True(t, has)
assert.True(t, emailAddress.IsPrimary)
- assert.EqualValues(t, user.IsActive, emailAddress.IsActivated)
- assert.EqualValues(t, user.ID, emailAddress.UID)
+ assert.Equal(t, user.IsActive, emailAddress.IsActivated)
+ assert.Equal(t, user.ID, emailAddress.UID)
}
}
diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go
index 9ca500c0f9..f53ff11df9 100644
--- a/models/migrations/v1_15/v182.go
+++ b/models/migrations/v1_15/v182.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go
index 75ef8e1cd8..5fc6a0c467 100644
--- a/models/migrations/v1_15/v182_test.go
+++ b/models/migrations/v1_15/v182_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"testing"
diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go
index effad1b467..5d0582f03d 100644
--- a/models/migrations/v1_15/v183.go
+++ b/models/migrations/v1_15/v183.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"fmt"
diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go
index 4b3dd1467a..2823bc1f7a 100644
--- a/models/migrations/v1_15/v184.go
+++ b/models/migrations/v1_15/v184.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"context"
diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go
index e5878ec193..60af59edca 100644
--- a/models/migrations/v1_15/v185.go
+++ b/models/migrations/v1_15/v185.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go
index 01aab3add5..67dc97d13d 100644
--- a/models/migrations/v1_15/v186.go
+++ b/models/migrations/v1_15/v186.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go
index 21cd6772b7..5fd90c65fb 100644
--- a/models/migrations/v1_15/v187.go
+++ b/models/migrations/v1_15/v187.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go
index 71e45cab0e..4494e6ff05 100644
--- a/models/migrations/v1_15/v188.go
+++ b/models/migrations/v1_15/v188.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go
index 817a0c13a4..7f93d6e9e5 100644
--- a/models/migrations/v1_16/main_test.go
+++ b/models/migrations/v1_16/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go
index 5649645051..6bc99e58ab 100644
--- a/models/migrations/v1_16/v189.go
+++ b/models/migrations/v1_16/v189.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"encoding/binary"
diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go
index 32ef821d27..fb56ac8e11 100644
--- a/models/migrations/v1_16/v189_test.go
+++ b/models/migrations/v1_16/v189_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -75,8 +75,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) {
return
}
- assert.EqualValues(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID)
- assert.EqualValues(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID)
+ assert.Equal(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID)
+ assert.Equal(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID)
}
}
}
diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go
index 5953802849..1eb6b6ddb4 100644
--- a/models/migrations/v1_16/v190.go
+++ b/models/migrations/v1_16/v190.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go
index c618783c08..957c82e484 100644
--- a/models/migrations/v1_16/v191.go
+++ b/models/migrations/v1_16/v191.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go
index 2d5d158a09..9d03fbe3c8 100644
--- a/models/migrations/v1_16/v192.go
+++ b/models/migrations/v1_16/v192.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go
index 8d3ce7a558..a5af2de380 100644
--- a/models/migrations/v1_16/v193.go
+++ b/models/migrations/v1_16/v193.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go
index b279967a2c..2e827f0550 100644
--- a/models/migrations/v1_16/v193_test.go
+++ b/models/migrations/v1_16/v193_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -62,7 +62,7 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
has, err := x.ID(attach.IssueID).Get(&issue)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, attach.RepoID, issue.RepoID)
+ assert.Equal(t, attach.RepoID, issue.RepoID)
}
var releaseAttachments []*NewAttachment
@@ -75,6 +75,6 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
has, err := x.ID(attach.ReleaseID).Get(&release)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, attach.RepoID, release.RepoID)
+ assert.Equal(t, attach.RepoID, release.RepoID)
}
}
diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go
index 6aa13c50cf..2e4ed8340e 100644
--- a/models/migrations/v1_16/v194.go
+++ b/models/migrations/v1_16/v194.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go
index 6d7e94141e..4fd42b7bd2 100644
--- a/models/migrations/v1_16/v195.go
+++ b/models/migrations/v1_16/v195.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go
index 742397bf32..946e06e399 100644
--- a/models/migrations/v1_16/v195_test.go
+++ b/models/migrations/v1_16/v195_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go
index 7cbafc61e5..6c9caa100f 100644
--- a/models/migrations/v1_16/v196.go
+++ b/models/migrations/v1_16/v196.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go
index 97888b2847..862bdfdcbd 100644
--- a/models/migrations/v1_16/v197.go
+++ b/models/migrations/v1_16/v197.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go
index 115bb313a0..f35ede138a 100644
--- a/models/migrations/v1_16/v198.go
+++ b/models/migrations/v1_16/v198.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go
index 6adcf890af..4020352f2b 100644
--- a/models/migrations/v1_16/v199.go
+++ b/models/migrations/v1_16/v199.go
@@ -1,6 +1,6 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
// We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now.
diff --git a/models/migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go
index c08c20e51d..de57fad8fe 100644
--- a/models/migrations/v1_16/v200.go
+++ b/models/migrations/v1_16/v200.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go
index 35e0c9f2fb..2c43698b0c 100644
--- a/models/migrations/v1_16/v201.go
+++ b/models/migrations/v1_16/v201.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go
index 6ba36152f1..d8c8fdcadc 100644
--- a/models/migrations/v1_16/v202.go
+++ b/models/migrations/v1_16/v202.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go
index e8e6b52453..c3241cba57 100644
--- a/models/migrations/v1_16/v203.go
+++ b/models/migrations/v1_16/v203.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go
index ece03e1305..4d375307e7 100644
--- a/models/migrations/v1_16/v204.go
+++ b/models/migrations/v1_16/v204.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go
index d6c577083c..78241bad5b 100644
--- a/models/migrations/v1_16/v205.go
+++ b/models/migrations/v1_16/v205.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go
index 581a7d76e9..01a9c386eb 100644
--- a/models/migrations/v1_16/v206.go
+++ b/models/migrations/v1_16/v206.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go
index 91208f066c..19126ead1f 100644
--- a/models/migrations/v1_16/v207.go
+++ b/models/migrations/v1_16/v207.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go
index 1a11ef096a..fb643324f4 100644
--- a/models/migrations/v1_16/v208.go
+++ b/models/migrations/v1_16/v208.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go
index be3100e02a..230838647b 100644
--- a/models/migrations/v1_16/v209.go
+++ b/models/migrations/v1_16/v209.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go
index 51b7d81e99..0b94baf8e3 100644
--- a/models/migrations/v1_16/v210.go
+++ b/models/migrations/v1_16/v210.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"encoding/base32"
diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go
index d43fb03106..3b4ac7aa4b 100644
--- a/models/migrations/v1_16/v210_test.go
+++ b/models/migrations/v1_16/v210_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -71,5 +71,5 @@ func Test_RemigrateU2FCredentials(t *testing.T) {
return
}
- assert.EqualValues(t, expected, got)
+ assert.Equal(t, expected, got)
}
diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go
index 79cb3fa078..571a4f55a3 100644
--- a/models/migrations/v1_17/main_test.go
+++ b/models/migrations/v1_17/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"testing"
diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go
index 9b72c8610b..517cf19388 100644
--- a/models/migrations/v1_17/v211.go
+++ b/models/migrations/v1_17/v211.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go
index e3f9437121..788792211f 100644
--- a/models/migrations/v1_17/v212.go
+++ b/models/migrations/v1_17/v212.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go
index bb3f466e52..b2bbdf7279 100644
--- a/models/migrations/v1_17/v213.go
+++ b/models/migrations/v1_17/v213.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go
index 2268164919..1925324f0f 100644
--- a/models/migrations/v1_17/v214.go
+++ b/models/migrations/v1_17/v214.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go
index b338f85417..748539225d 100644
--- a/models/migrations/v1_17/v215.go
+++ b/models/migrations/v1_17/v215.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/models/pull"
diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go
index 268f472a42..37aeacb6fc 100644
--- a/models/migrations/v1_17/v216.go
+++ b/models/migrations/v1_17/v216.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
// This migration added non-ideal indices to the action table which on larger datasets slowed things down
// it has been superseded by v218.go
diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go
index 3f970b68a5..04626bcbc5 100644
--- a/models/migrations/v1_17/v217.go
+++ b/models/migrations/v1_17/v217.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go
index 4c05a9b539..17d4cd89d4 100644
--- a/models/migrations/v1_17/v218.go
+++ b/models/migrations/v1_17/v218.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go
index d266029fd9..6e335cb813 100644
--- a/models/migrations/v1_17/v219.go
+++ b/models/migrations/v1_17/v219.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"time"
diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go
index 904ddc5192..4ac8c58e1e 100644
--- a/models/migrations/v1_17/v220.go
+++ b/models/migrations/v1_17/v220.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
packages_model "code.gitea.io/gitea/models/packages"
diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go
index 9e159388bd..9e6a67eb18 100644
--- a/models/migrations/v1_17/v221.go
+++ b/models/migrations/v1_17/v221.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go
index 9ca54142e2..a2dc0fae55 100644
--- a/models/migrations/v1_17/v221_test.go
+++ b/models/migrations/v1_17/v221_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go
index 2ffb94eb1c..a5ea537d8a 100644
--- a/models/migrations/v1_17/v222.go
+++ b/models/migrations/v1_17/v222.go
@@ -1,10 +1,11 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/base"
@@ -29,7 +30,7 @@ func DropOldCredentialIDColumn(x *xorm.Engine) error {
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
- return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
+ return errors.New("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
}
// Create webauthnCredential table
diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go
index 018451ee4c..b2bfb76551 100644
--- a/models/migrations/v1_17/v223.go
+++ b/models/migrations/v1_17/v223.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"context"
diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go
index f71a21d1fb..ebcfb45a94 100644
--- a/models/migrations/v1_18/main_test.go
+++ b/models/migrations/v1_18/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go
index f3d522b91a..6dc12020ea 100644
--- a/models/migrations/v1_18/v224.go
+++ b/models/migrations/v1_18/v224.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go
index b0ac3777fc..bc6117e38f 100644
--- a/models/migrations/v1_18/v225.go
+++ b/models/migrations/v1_18/v225.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go
index f87e24b11d..8ed9761476 100644
--- a/models/migrations/v1_18/v226.go
+++ b/models/migrations/v1_18/v226.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go
index 5fe5dcd0c9..3aca686d59 100644
--- a/models/migrations/v1_18/v227.go
+++ b/models/migrations/v1_18/v227.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go
index 3e7a36de15..b13f6461bd 100644
--- a/models/migrations/v1_18/v228.go
+++ b/models/migrations/v1_18/v228.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go
index 10d9f35097..bc15e01390 100644
--- a/models/migrations/v1_18/v229.go
+++ b/models/migrations/v1_18/v229.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"fmt"
diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go
index d489328c00..5722dd3557 100644
--- a/models/migrations/v1_18/v229_test.go
+++ b/models/migrations/v1_18/v229_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go
index ea5b4d02e1..078fce7643 100644
--- a/models/migrations/v1_18/v230.go
+++ b/models/migrations/v1_18/v230.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go
index 40db4c2ffe..25b2f6525d 100644
--- a/models/migrations/v1_18/v230_test.go
+++ b/models/migrations/v1_18/v230_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go
index 59f42af111..87e807be6e 100644
--- a/models/migrations/v1_19/main_test.go
+++ b/models/migrations/v1_19/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"testing"
diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go
index 79e46132f0..8ef1e4e743 100644
--- a/models/migrations/v1_19/v231.go
+++ b/models/migrations/v1_19/v231.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go
index 9caf587c1e..493dbc6df8 100644
--- a/models/migrations/v1_19/v232.go
+++ b/models/migrations/v1_19/v232.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go
index ba4cd8e20b..9eb6d40509 100644
--- a/models/migrations/v1_19/v233.go
+++ b/models/migrations/v1_19/v233.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"fmt"
diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go
index 32c10ab0f4..7436ff7483 100644
--- a/models/migrations/v1_19/v233_test.go
+++ b/models/migrations/v1_19/v233_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"testing"
@@ -64,7 +64,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) {
assert.Equal(t, e.Meta, got[i].Meta)
if e.HeaderAuthorization == "" {
- assert.Equal(t, "", got[i].HeaderAuthorizationEncrypted)
+ assert.Empty(t, got[i].HeaderAuthorizationEncrypted)
} else {
cipherhex := got[i].HeaderAuthorizationEncrypted
cleartext, err := secret.DecryptSecret(setting.SecretKey, cipherhex)
diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go
index 728a580807..3475384d6f 100644
--- a/models/migrations/v1_19/v234.go
+++ b/models/migrations/v1_19/v234.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go
index 3715de3920..297d90f65a 100644
--- a/models/migrations/v1_19/v235.go
+++ b/models/migrations/v1_19/v235.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go
index f172a85b1f..0ed4d97a27 100644
--- a/models/migrations/v1_19/v236.go
+++ b/models/migrations/v1_19/v236.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go
index b23c765aa5..cf30226ccd 100644
--- a/models/migrations/v1_19/v237.go
+++ b/models/migrations/v1_19/v237.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go
index 266e6cea58..de681bfc7a 100644
--- a/models/migrations/v1_19/v238.go
+++ b/models/migrations/v1_19/v238.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go
index 10076f2401..8f4a65be95 100644
--- a/models/migrations/v1_19/v239.go
+++ b/models/migrations/v1_19/v239.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go
index 4505f86299..7fdbaeb9dc 100644
--- a/models/migrations/v1_19/v240.go
+++ b/models/migrations/v1_19/v240.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/models/db"
diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go
index a617d6fd2f..e35801a057 100644
--- a/models/migrations/v1_19/v241.go
+++ b/models/migrations/v1_19/v241.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go
index 4470835214..e9e759eaaa 100644
--- a/models/migrations/v1_19/v242.go
+++ b/models/migrations/v1_19/v242.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go
index 55bbfafb2f..9c3f372594 100644
--- a/models/migrations/v1_19/v243.go
+++ b/models/migrations/v1_19/v243.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go
index 92a1a9f622..2fd63a7118 100644
--- a/models/migrations/v1_20/main_test.go
+++ b/models/migrations/v1_20/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"testing"
diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go
index 977566ad7d..76cdccaca5 100644
--- a/models/migrations/v1_20/v244.go
+++ b/models/migrations/v1_20/v244.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go
index ab58d12880..4acb11416c 100644
--- a/models/migrations/v1_20/v245.go
+++ b/models/migrations/v1_20/v245.go
@@ -1,11 +1,10 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"context"
- "fmt"
"code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/setting"
@@ -57,7 +56,7 @@ func RenameWebhookOrgToOwner(x *xorm.Engine) error {
return err
}
sqlType := x.Dialect().SQLType(inferredTable.GetColumn("org_id"))
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE `webhook` CHANGE org_id owner_id " + sqlType); err != nil {
return err
}
case setting.Database.Type.IsMSSQL():
diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go
index e6340ef079..22bf723404 100644
--- a/models/migrations/v1_20/v246.go
+++ b/models/migrations/v1_20/v246.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go
index 59fc5c46b5..4f82937e18 100644
--- a/models/migrations/v1_20/v247.go
+++ b/models/migrations/v1_20/v247.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go
index 40555210e7..4f2091e4bc 100644
--- a/models/migrations/v1_20/v248.go
+++ b/models/migrations/v1_20/v248.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import "xorm.io/xorm"
diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go
index 02951a74d6..c6d3a177ca 100644
--- a/models/migrations/v1_20/v249.go
+++ b/models/migrations/v1_20/v249.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go
index 86388ef0b8..ec45e6e5c3 100644
--- a/models/migrations/v1_20/v250.go
+++ b/models/migrations/v1_20/v250.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"strings"
diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go
index 7743248a3f..a274c22a73 100644
--- a/models/migrations/v1_20/v251.go
+++ b/models/migrations/v1_20/v251.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go
index ab61cd9b8b..d6aa602753 100644
--- a/models/migrations/v1_20/v252.go
+++ b/models/migrations/v1_20/v252.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go
index 96c494bd8d..c96454dbf9 100644
--- a/models/migrations/v1_20/v253.go
+++ b/models/migrations/v1_20/v253.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go
index 1e26979a5b..9cdbfb3916 100644
--- a/models/migrations/v1_20/v254.go
+++ b/models/migrations/v1_20/v254.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go
index 14b70f8f96..caf198700e 100644
--- a/models/migrations/v1_20/v255.go
+++ b/models/migrations/v1_20/v255.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go
index 822153b93e..7b84c1e154 100644
--- a/models/migrations/v1_20/v256.go
+++ b/models/migrations/v1_20/v256.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go
index 6c6ca4c748..9d5f7c07df 100644
--- a/models/migrations/v1_20/v257.go
+++ b/models/migrations/v1_20/v257.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go
index 47174ce805..1d3faffdae 100644
--- a/models/migrations/v1_20/v258.go
+++ b/models/migrations/v1_20/v258.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go
index 5b8ced4ad7..9e0dc9b61d 100644
--- a/models/migrations/v1_20/v259.go
+++ b/models/migrations/v1_20/v259.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"fmt"
@@ -329,7 +329,7 @@ func ConvertScopedAccessTokens(x *xorm.Engine) error {
for _, token := range tokens {
var scopes []string
allNewScopesMap := make(map[AccessTokenScope]bool)
- for _, oldScope := range strings.Split(token.Scope, ",") {
+ for oldScope := range strings.SplitSeq(token.Scope, ",") {
if newScopes, exists := accessTokenScopeMap[OldAccessTokenScope(oldScope)]; exists {
for _, newScope := range newScopes {
allNewScopesMap[newScope] = true
diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go
index 5bc9a71391..0bf63719e5 100644
--- a/models/migrations/v1_20/v259_test.go
+++ b/models/migrations/v1_20/v259_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"sort"
@@ -96,7 +96,7 @@ func Test_ConvertScopedAccessTokens(t *testing.T) {
tokens := make([]AccessToken, 0)
err = x.Find(&tokens)
assert.NoError(t, err)
- assert.Equal(t, len(tests), len(tokens))
+ assert.Len(t, tokens, len(tests))
// sort the tokens (insertion order by auto-incrementing primary key)
sort.Slice(tokens, func(i, j int) bool {
diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go
index 9afdea1677..536a7ade08 100644
--- a/models/migrations/v1_21/main_test.go
+++ b/models/migrations/v1_21/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"testing"
diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go
index 6ca52c5998..8540c58ae8 100644
--- a/models/migrations/v1_21/v260.go
+++ b/models/migrations/v1_21/v260.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go
index 4ec1160d0b..122b98eb93 100644
--- a/models/migrations/v1_21/v261.go
+++ b/models/migrations/v1_21/v261.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go
index 23e900572a..6e88e29b9d 100644
--- a/models/migrations/v1_21/v262.go
+++ b/models/migrations/v1_21/v262.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go
index 2c7cbadf0d..55c418bde0 100644
--- a/models/migrations/v1_21/v263.go
+++ b/models/migrations/v1_21/v263.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"fmt"
diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go
index e81a17ad6d..7fc0ec6024 100644
--- a/models/migrations/v1_21/v264.go
+++ b/models/migrations/v1_21/v264.go
@@ -1,11 +1,11 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
@@ -57,7 +57,7 @@ func AddBranchTable(x *xorm.Engine) error {
if err != nil {
return err
} else if !has {
- return fmt.Errorf("no admin user found")
+ return errors.New("no admin user found")
}
branches := make([]Branch, 0, 100)
diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go
index 800eb95f72..b6892acc27 100644
--- a/models/migrations/v1_21/v265.go
+++ b/models/migrations/v1_21/v265.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go
index 79a5f5e14c..440549e868 100644
--- a/models/migrations/v1_21/v266.go
+++ b/models/migrations/v1_21/v266.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go
index bc0e954bdc..394139a17e 100644
--- a/models/migrations/v1_21/v267.go
+++ b/models/migrations/v1_21/v267.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go
index 332793ff07..b677d2383e 100644
--- a/models/migrations/v1_21/v268.go
+++ b/models/migrations/v1_21/v268.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go
index 475ec02380..042040927d 100644
--- a/models/migrations/v1_21/v269.go
+++ b/models/migrations/v1_21/v269.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go
index b9cc84d3ac..ab7c5660ba 100644
--- a/models/migrations/v1_21/v270.go
+++ b/models/migrations/v1_21/v270.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go
index 098f6499d5..05e1af1351 100644
--- a/models/migrations/v1_21/v271.go
+++ b/models/migrations/v1_21/v271.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go
index a729c49f1b..14c1e0c4b0 100644
--- a/models/migrations/v1_21/v272.go
+++ b/models/migrations/v1_21/v272.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go
index 61c79f4a76..e614a56a7d 100644
--- a/models/migrations/v1_21/v273.go
+++ b/models/migrations/v1_21/v273.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go
index df5994f159..d0b557a151 100644
--- a/models/migrations/v1_21/v274.go
+++ b/models/migrations/v1_21/v274.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"time"
diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go
index 78804a59d6..2bfe5c72fa 100644
--- a/models/migrations/v1_21/v275.go
+++ b/models/migrations/v1_21/v275.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go
index 15177bf040..3ab7e22cd0 100644
--- a/models/migrations/v1_21/v276.go
+++ b/models/migrations/v1_21/v276.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"context"
@@ -172,7 +172,7 @@ func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
}
- u, err := giturl.Parse(remoteURL)
+ u, err := giturl.ParseGitURL(remoteURL)
if err != nil {
return "", err
}
diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go
index 12529160b7..0c102eddde 100644
--- a/models/migrations/v1_21/v277.go
+++ b/models/migrations/v1_21/v277.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go
index d6a462d1e7..846f228678 100644
--- a/models/migrations/v1_21/v278.go
+++ b/models/migrations/v1_21/v278.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go
index 2abd1bbe84..beb39effe1 100644
--- a/models/migrations/v1_21/v279.go
+++ b/models/migrations/v1_21/v279.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go
index efd8dbaa8c..ac8facd6aa 100644
--- a/models/migrations/v1_22/main_test.go
+++ b/models/migrations/v1_22/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go
index a8ee4a3bf7..2271cb6089 100644
--- a/models/migrations/v1_22/v280.go
+++ b/models/migrations/v1_22/v280.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go
index fc1866aa83..129ec2cba0 100644
--- a/models/migrations/v1_22/v281.go
+++ b/models/migrations/v1_22/v281.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go
index baad9e0916..eed64c30f7 100644
--- a/models/migrations/v1_22/v282.go
+++ b/models/migrations/v1_22/v282.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go
index 0a45c51245..0eca031b65 100644
--- a/models/migrations/v1_22/v283.go
+++ b/models/migrations/v1_22/v283.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"fmt"
diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go
index e89a7cbfc2..743f860466 100644
--- a/models/migrations/v1_22/v283_test.go
+++ b/models/migrations/v1_22/v283_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go
index 2b95078980..31b38f6aed 100644
--- a/models/migrations/v1_22/v284.go
+++ b/models/migrations/v1_22/v284.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
+
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go
index a55cc17c04..fed89f670e 100644
--- a/models/migrations/v1_22/v285.go
+++ b/models/migrations/v1_22/v285.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"time"
diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go
index 1fcde33202..f3ba50dbb6 100644
--- a/models/migrations/v1_22/v286.go
+++ b/models/migrations/v1_22/v286.go
@@ -1,6 +1,6 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"errors"
diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go
index 1f213ddb6e..b4a50f6fcb 100644
--- a/models/migrations/v1_22/v286_test.go
+++ b/models/migrations/v1_22/v286_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
@@ -108,11 +108,11 @@ func Test_RepositoryFormat(t *testing.T) {
ok, err := x.ID(2).Get(repo)
assert.NoError(t, err)
assert.True(t, ok)
- assert.EqualValues(t, "sha1", repo.ObjectFormatName)
+ assert.Equal(t, "sha1", repo.ObjectFormatName)
repo = new(Repository)
ok, err = x.ID(id).Get(repo)
assert.NoError(t, err)
assert.True(t, ok)
- assert.EqualValues(t, "sha256", repo.ObjectFormatName)
+ assert.Equal(t, "sha256", repo.ObjectFormatName)
}
diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go
index c8b1593286..5fd901f9de 100644
--- a/models/migrations/v1_22/v287.go
+++ b/models/migrations/v1_22/v287.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v287_test.go b/models/migrations/v1_22/v287_test.go
index 9c7b10947d..2b42a33c38 100644
--- a/models/migrations/v1_22/v287_test.go
+++ b/models/migrations/v1_22/v287_test.go
@@ -1,10 +1,10 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
- "fmt"
+ "strconv"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -50,7 +50,7 @@ func Test_UpdateBadgeColName(t *testing.T) {
for i, e := range oldBadges {
got := got[i+1] // 1 is in the badge.yml
assert.Equal(t, e.ID, got.ID)
- assert.Equal(t, fmt.Sprintf("%d", e.ID), got.Slug)
+ assert.Equal(t, strconv.FormatInt(e.ID, 10), got.Slug)
}
// TODO: check if badges have been updated
diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go
index 7c93bfcc66..26c850c218 100644
--- a/models/migrations/v1_22/v288.go
+++ b/models/migrations/v1_22/v288.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go
index b9941aadd9..78689a4ffa 100644
--- a/models/migrations/v1_22/v289.go
+++ b/models/migrations/v1_22/v289.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go
index 9c54d4e87c..0f4d78410c 100644
--- a/models/migrations/v1_22/v290.go
+++ b/models/migrations/v1_22/v290.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go
index 74726fae96..823a644a95 100644
--- a/models/migrations/v1_22/v291.go
+++ b/models/migrations/v1_22/v291.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go
index beca556aee..440f48ce80 100644
--- a/models/migrations/v1_22/v292.go
+++ b/models/migrations/v1_22/v292.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
// NOTE: noop the original migration has bug which some projects will be skip, so
// these projects will have no default board.
diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go
index 53cc719294..5299b8618f 100644
--- a/models/migrations/v1_22/v293.go
+++ b/models/migrations/v1_22/v293.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go
index cfe4345143..2c8f7683a8 100644
--- a/models/migrations/v1_22/v293_test.go
+++ b/models/migrations/v1_22/v293_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go
index 20e261fb1b..8776e51a16 100644
--- a/models/migrations/v1_22/v294.go
+++ b/models/migrations/v1_22/v294.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"fmt"
diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go
index a1d702cb77..1cf03d6120 100644
--- a/models/migrations/v1_22/v294_test.go
+++ b/models/migrations/v1_22/v294_test.go
@@ -1,10 +1,9 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
- "slices"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -44,7 +43,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
for _, index := range tables[0].Indexes {
if index.Type == schemas.UniqueType {
found = true
- slices.Equal(index.Cols, []string{"project_id", "issue_id"})
+ assert.ElementsMatch(t, index.Cols, []string{"project_id", "issue_id"})
break
}
}
diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go
index 17bdadb4ad..319b1a399b 100644
--- a/models/migrations/v1_22/v295.go
+++ b/models/migrations/v1_22/v295.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go
index 1ecacab95f..75350f9f65 100644
--- a/models/migrations/v1_22/v296.go
+++ b/models/migrations/v1_22/v296.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v297.go b/models/migrations/v1_22/v297.go
index 7d4b506925..9a4405f266 100644
--- a/models/migrations/v1_22/v297.go
+++ b/models/migrations/v1_22/v297.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/models/perm"
diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go
index b9f3b95ade..7700173a00 100644
--- a/models/migrations/v1_22/v298.go
+++ b/models/migrations/v1_22/v298.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go
index b7948bd4dd..f7b2caed83 100644
--- a/models/migrations/v1_23/main_test.go
+++ b/models/migrations/v1_23/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"testing"
diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go
index f6db960c3b..11021d8855 100644
--- a/models/migrations/v1_23/v299.go
+++ b/models/migrations/v1_23/v299.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -14,5 +14,9 @@ func AddContentVersionToIssueAndComment(x *xorm.Engine) error {
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
}
- return x.Sync(new(Comment), new(Issue))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Comment), new(Issue))
+ return err
}
diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go
index f1f1cccdbf..13c6489c5e 100644
--- a/models/migrations/v1_23/v300.go
+++ b/models/migrations/v1_23/v300.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -13,5 +13,9 @@ func AddForcePushBranchProtection(x *xorm.Engine) error {
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go
index b7797f6c6b..ed8e9ef059 100644
--- a/models/migrations/v1_23/v301.go
+++ b/models/migrations/v1_23/v301.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -10,5 +10,9 @@ func AddSkipSecondaryAuthColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
type oauth2Application struct {
SkipSecondaryAuthorization bool `xorm:"NOT NULL DEFAULT FALSE"`
}
- return x.Sync(new(oauth2Application))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(oauth2Application))
+ return err
}
diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go
index d7ea03eb3d..e4a50b3ec8 100644
--- a/models/migrations/v1_23/v302.go
+++ b/models/migrations/v1_23/v302.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
@@ -14,5 +14,8 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error {
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
LogExpired bool `xorm:"index(stopped_log_expired)"`
}
- return x.Sync(new(ActionTask))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreDropIndices: true,
+ }, new(ActionTask))
+ return err
}
diff --git a/models/migrations/v1_23/v302_test.go b/models/migrations/v1_23/v302_test.go
new file mode 100644
index 0000000000..b008b6fc03
--- /dev/null
+++ b/models/migrations/v1_23/v302_test.go
@@ -0,0 +1,51 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/migrations/base"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddIndexToActionTaskStoppedLogExpired(t *testing.T) {
+ type ActionTask struct {
+ ID int64
+ JobID int64
+ Attempt int64
+ RunnerID int64 `xorm:"index"`
+ Status int `xorm:"index"`
+ Started timeutil.TimeStamp `xorm:"index"`
+ Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
+
+ RepoID int64 `xorm:"index"`
+ OwnerID int64 `xorm:"index"`
+ CommitSHA string `xorm:"index"`
+ IsForkPullRequest bool
+
+ Token string `xorm:"-"`
+ TokenHash string `xorm:"UNIQUE"` // sha256 of token
+ TokenSalt string
+ TokenLastEight string `xorm:"index token_last_eight"`
+
+ LogFilename string // file name of log
+ LogInStorage bool // read log from database or from storage
+ LogLength int64 // lines count
+ LogSize int64 // blob size
+ LogIndexes []int64 `xorm:"LONGBLOB"` // line number to offset
+ LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted
+
+ Created timeutil.TimeStamp `xorm:"created"`
+ Updated timeutil.TimeStamp `xorm:"updated index"`
+ }
+
+ // Prepare and load the testing database
+ x, deferable := base.PrepareTestEnv(t, 0, new(ActionTask))
+ defer deferable()
+
+ assert.NoError(t, AddIndexToActionTaskStoppedLogExpired(x))
+}
diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go
index adfe917d3f..dc541a9535 100644
--- a/models/migrations/v1_23/v303.go
+++ b/models/migrations/v1_23/v303.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -19,5 +19,9 @@ func AddCommentMetaDataColumn(x *xorm.Engine) error {
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
}
- return x.Sync(new(Comment))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Comment))
+ return err
}
diff --git a/models/migrations/v1_23/v304.go b/models/migrations/v1_23/v304.go
index 65cffedbd9..35d4d4881a 100644
--- a/models/migrations/v1_23/v304.go
+++ b/models/migrations/v1_23/v304.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -9,5 +9,8 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error {
type Release struct {
Sha1 string `xorm:"INDEX VARCHAR(64)"`
}
- return x.Sync(new(Release))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreDropIndices: true,
+ }, new(Release))
+ return err
}
diff --git a/models/migrations/v1_23/v304_test.go b/models/migrations/v1_23/v304_test.go
new file mode 100644
index 0000000000..c3dfa5e7e7
--- /dev/null
+++ b/models/migrations/v1_23/v304_test.go
@@ -0,0 +1,40 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/migrations/base"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddIndexForReleaseSha1(t *testing.T) {
+ type Release struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX UNIQUE(n)"`
+ PublisherID int64 `xorm:"INDEX"`
+ TagName string `xorm:"INDEX UNIQUE(n)"`
+ OriginalAuthor string
+ OriginalAuthorID int64 `xorm:"index"`
+ LowerTagName string
+ Target string
+ Title string
+ Sha1 string `xorm:"VARCHAR(64)"`
+ NumCommits int64
+ Note string `xorm:"TEXT"`
+ IsDraft bool `xorm:"NOT NULL DEFAULT false"`
+ IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
+ IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
+ }
+
+ // Prepare and load the testing database
+ x, deferable := base.PrepareTestEnv(t, 0, new(Release))
+ defer deferable()
+
+ assert.NoError(t, AddIndexForReleaseSha1(x))
+}
diff --git a/models/migrations/v1_23/v305.go b/models/migrations/v1_23/v305.go
index 4d881192b2..3762279de1 100644
--- a/models/migrations/v1_23/v305.go
+++ b/models/migrations/v1_23/v305.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v306.go b/models/migrations/v1_23/v306.go
index 276b438e95..c5c89dbeb8 100644
--- a/models/migrations/v1_23/v306.go
+++ b/models/migrations/v1_23/v306.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -9,5 +9,9 @@ func AddBlockAdminMergeOverrideBranchProtection(x *xorm.Engine) error {
type ProtectedBranch struct {
BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v307.go b/models/migrations/v1_23/v307.go
index ef7f5f2c3f..54a69d250b 100644
--- a/models/migrations/v1_23/v307.go
+++ b/models/migrations/v1_23/v307.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v308.go b/models/migrations/v1_23/v308.go
index 1e8a9b0af2..695fdfcc2d 100644
--- a/models/migrations/v1_23/v308.go
+++ b/models/migrations/v1_23/v308.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v309.go b/models/migrations/v1_23/v309.go
index 5b39398443..e629b718a8 100644
--- a/models/migrations/v1_23/v309.go
+++ b/models/migrations/v1_23/v309.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v310.go b/models/migrations/v1_23/v310.go
index 394417f5a0..074b1c54d3 100644
--- a/models/migrations/v1_23/v310.go
+++ b/models/migrations/v1_23/v310.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -12,5 +12,9 @@ func AddPriorityToProtectedBranch(x *xorm.Engine) error {
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v311.go b/models/migrations/v1_23/v311.go
index 0fc1ac8c0e..ef48085c79 100644
--- a/models/migrations/v1_23/v311.go
+++ b/models/migrations/v1_23/v311.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -11,6 +11,9 @@ func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
type Issue struct {
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
}
-
- return x.Sync(new(Issue))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Issue))
+ return err
}
diff --git a/models/migrations/v1_24/v312.go b/models/migrations/v1_24/v312.go
new file mode 100644
index 0000000000..823b0eae40
--- /dev/null
+++ b/models/migrations/v1_24/v312.go
@@ -0,0 +1,25 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+type pullAutoMerge struct {
+ DeleteBranchAfterMerge bool
+}
+
+// TableName return database table name for xorm
+func (pullAutoMerge) TableName() string {
+ return "pull_auto_merge"
+}
+
+func AddDeleteBranchAfterMergeForAutoMerge(x *xorm.Engine) error {
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(pullAutoMerge))
+ return err
+}
diff --git a/models/migrations/v1_24/v313.go b/models/migrations/v1_24/v313.go
new file mode 100644
index 0000000000..7e6cda6bfd
--- /dev/null
+++ b/models/migrations/v1_24/v313.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/models/migrations/base"
+
+ "xorm.io/xorm"
+)
+
+func MovePinOrderToTableIssuePin(x *xorm.Engine) error {
+ type IssuePin struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IssueID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IsPull bool `xorm:"NOT NULL"`
+ PinOrder int `xorm:"DEFAULT 0"`
+ }
+
+ if err := x.Sync(new(IssuePin)); err != nil {
+ return err
+ }
+
+ if _, err := x.Exec("INSERT INTO issue_pin (repo_id, issue_id, is_pull, pin_order) SELECT repo_id, id, is_pull, pin_order FROM issue WHERE pin_order > 0"); err != nil {
+ return err
+ }
+ sess := x.NewSession()
+ defer sess.Close()
+ return base.DropTableColumns(sess, "issue", "pin_order")
+}
diff --git a/models/migrations/v1_24/v314.go b/models/migrations/v1_24/v314.go
new file mode 100644
index 0000000000..51cb2e34aa
--- /dev/null
+++ b/models/migrations/v1_24/v314.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func UpdateOwnerIDOfRepoLevelActionsTables(x *xorm.Engine) error {
+ if _, err := x.Exec("UPDATE `action_runner` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0"); err != nil {
+ return err
+ }
+ if _, err := x.Exec("UPDATE `action_variable` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0"); err != nil {
+ return err
+ }
+ _, err := x.Exec("UPDATE `secret` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ return err
+}
diff --git a/models/migrations/v1_24/v315.go b/models/migrations/v1_24/v315.go
new file mode 100644
index 0000000000..52b9b44785
--- /dev/null
+++ b/models/migrations/v1_24/v315.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddEphemeralToActionRunner(x *xorm.Engine) error {
+ type ActionRunner struct {
+ Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ActionRunner))
+ return err
+}
diff --git a/models/migrations/v1_24/v316.go b/models/migrations/v1_24/v316.go
new file mode 100644
index 0000000000..14e888f9ee
--- /dev/null
+++ b/models/migrations/v1_24/v316.go
@@ -0,0 +1,24 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddDescriptionForSecretsAndVariables(x *xorm.Engine) error {
+ type Secret struct {
+ Description string `xorm:"TEXT"`
+ }
+
+ type ActionVariable struct {
+ Description string `xorm:"TEXT"`
+ }
+
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Secret), new(ActionVariable))
+ return err
+}
diff --git a/models/migrations/v1_24/v317.go b/models/migrations/v1_24/v317.go
new file mode 100644
index 0000000000..a13db2dd27
--- /dev/null
+++ b/models/migrations/v1_24/v317.go
@@ -0,0 +1,56 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+ "xorm.io/xorm/schemas"
+)
+
+type improveActionTableIndicesAction struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"INDEX"` // Receiver user id.
+ OpType int
+ ActUserID int64 // Action user id.
+ RepoID int64
+ CommentID int64 `xorm:"INDEX"`
+ IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
+ RefName string
+ IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
+ Content string `xorm:"TEXT"`
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
+}
+
+// TableName sets the name of this table
+func (*improveActionTableIndicesAction) TableName() string {
+ return "action"
+}
+
+// TableIndices implements xorm's TableIndices interface
+func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
+ repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
+ repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
+
+ actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
+ actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
+
+ cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
+ cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
+
+ cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
+ cuIndex.AddColumn("user_id", "is_deleted")
+
+ actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
+ actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
+
+ indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
+
+ return indices
+}
+
+func AddNewIndexForUserDashboard(x *xorm.Engine) error {
+ return x.Sync(new(improveActionTableIndicesAction))
+}
diff --git a/models/migrations/v1_24/v318.go b/models/migrations/v1_24/v318.go
new file mode 100644
index 0000000000..9b4a540960
--- /dev/null
+++ b/models/migrations/v1_24/v318.go
@@ -0,0 +1,21 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/models/perm"
+
+ "xorm.io/xorm"
+)
+
+func AddRepoUnitAnonymousAccessMode(x *xorm.Engine) error {
+ type RepoUnit struct { //revive:disable-line:exported
+ AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(RepoUnit))
+ return err
+}
diff --git a/models/migrations/v1_24/v319.go b/models/migrations/v1_24/v319.go
new file mode 100644
index 0000000000..648081f74e
--- /dev/null
+++ b/models/migrations/v1_24/v319.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddExclusiveOrderColumnToLabelTable(x *xorm.Engine) error {
+ type Label struct {
+ ExclusiveOrder int `xorm:"DEFAULT 0"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Label))
+ return err
+}
diff --git a/models/migrations/v1_24/v320.go b/models/migrations/v1_24/v320.go
new file mode 100644
index 0000000000..ebef71939c
--- /dev/null
+++ b/models/migrations/v1_24/v320.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/modules/json"
+
+ "xorm.io/xorm"
+)
+
+func MigrateSkipTwoFactor(x *xorm.Engine) error {
+ type LoginSource struct {
+ TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
+ }
+ _, err := x.SyncWithOptions(
+ xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ },
+ new(LoginSource),
+ )
+ if err != nil {
+ return err
+ }
+
+ type LoginSourceSimple struct {
+ ID int64
+ Cfg string
+ }
+
+ var loginSources []LoginSourceSimple
+ err = x.Table("login_source").Find(&loginSources)
+ if err != nil {
+ return err
+ }
+
+ for _, source := range loginSources {
+ if source.Cfg == "" {
+ continue
+ }
+
+ var cfg map[string]any
+ err = json.Unmarshal([]byte(source.Cfg), &cfg)
+ if err != nil {
+ return err
+ }
+
+ if cfg["SkipLocalTwoFA"] == true {
+ _, err = x.Exec("UPDATE login_source SET two_factor_policy = 'skip' WHERE id = ?", source.ID)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go
index 74434a84a1..41f0966942 100644
--- a/models/migrations/v1_6/v70.go
+++ b/models/migrations/v1_6/v70.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go
index 586187228b..2b11f57c92 100644
--- a/models/migrations/v1_6/v71.go
+++ b/models/migrations/v1_6/v71.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go
index 04cef9a170..9fad88a1b6 100644
--- a/models/migrations/v1_6/v72.go
+++ b/models/migrations/v1_6/v72.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go
index b5a748aae3..e0b7a28537 100644
--- a/models/migrations/v1_7/v73.go
+++ b/models/migrations/v1_7/v73.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go
index f0567e3c9b..376be37a24 100644
--- a/models/migrations/v1_7/v74.go
+++ b/models/migrations/v1_7/v74.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import "xorm.io/xorm"
diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go
index fa7430970c..ef11575466 100644
--- a/models/migrations/v1_7/v75.go
+++ b/models/migrations/v1_7/v75.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go
index d3fbd94deb..81e9307549 100644
--- a/models/migrations/v1_8/v76.go
+++ b/models/migrations/v1_8/v76.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"fmt"
diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go
index 8b19993924..4fe5ebe635 100644
--- a/models/migrations/v1_8/v77.go
+++ b/models/migrations/v1_8/v77.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go
index 8f041c1484..e67f464131 100644
--- a/models/migrations/v1_8/v78.go
+++ b/models/migrations/v1_8/v78.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go
index eb3a9ed0f4..3f50114d5a 100644
--- a/models/migrations/v1_8/v79.go
+++ b/models/migrations/v1_8/v79.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go
index cebbbead28..6f9df47a93 100644
--- a/models/migrations/v1_8/v80.go
+++ b/models/migrations/v1_8/v80.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import "xorm.io/xorm"
diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go
index a100dc1ef7..3c2acc6458 100644
--- a/models/migrations/v1_8/v81.go
+++ b/models/migrations/v1_8/v81.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"fmt"
diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go
index 26806dd645..c685d3b86b 100644
--- a/models/migrations/v1_9/v82.go
+++ b/models/migrations/v1_9/v82.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"fmt"
diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go
index 10e6c45875..a0cd57f7c5 100644
--- a/models/migrations/v1_9/v83.go
+++ b/models/migrations/v1_9/v83.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go
index c7155fe9cf..423915ae57 100644
--- a/models/migrations/v1_9/v84.go
+++ b/models/migrations/v1_9/v84.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go
index a23d7c5d6e..48e1cd5dc4 100644
--- a/models/migrations/v1_9/v85.go
+++ b/models/migrations/v1_9/v85.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"fmt"
diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go
index cf2725d158..9464ff0cf6 100644
--- a/models/migrations/v1_9/v86.go
+++ b/models/migrations/v1_9/v86.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go
index fa01b6e5e3..81a4ebf80d 100644
--- a/models/migrations/v1_9/v87.go
+++ b/models/migrations/v1_9/v87.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/organization/org.go b/models/organization/org.go
index 725a99356e..0f3aef146c 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -9,11 +9,8 @@ import (
"fmt"
"strings"
- actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -181,12 +178,6 @@ func (org *Organization) HomeLink() string {
return org.AsUser().HomeLink()
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (org *Organization) CanCreateRepo() bool {
- return org.AsUser().CanCreateRepo()
-}
-
// FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct {
db.ListOptions
@@ -407,33 +398,6 @@ func GetOrgByName(ctx context.Context, name string) (*Organization, error) {
return u, nil
}
-// DeleteOrganization deletes models associated to an organization.
-func DeleteOrganization(ctx context.Context, org *Organization) error {
- if org.Type != user_model.UserTypeOrganization {
- return fmt.Errorf("%s is a user not an organization", org.Name)
- }
-
- if err := db.DeleteBeans(ctx,
- &Team{OrgID: org.ID},
- &OrgUser{OrgID: org.ID},
- &TeamUser{OrgID: org.ID},
- &TeamUnit{OrgID: org.ID},
- &TeamInvite{OrgID: org.ID},
- &secret_model.Secret{OwnerID: org.ID},
- &user_model.Blocking{BlockerID: org.ID},
- &actions_model.ActionRunner{OwnerID: org.ID},
- &actions_model.ActionRunnerToken{OwnerID: org.ID},
- ); err != nil {
- return fmt.Errorf("DeleteBeans: %w", err)
- }
-
- if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
- return fmt.Errorf("Delete: %w", err)
- }
-
- return nil
-}
-
// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
func (org *Organization) GetOrgUserMaxAuthorizeLevel(ctx context.Context, uid int64) (perm.AccessMode, error) {
var authorize perm.AccessMode
@@ -604,7 +568,9 @@ func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
return err
}
-func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
+// GetUserTeams returns all teams that belong to user,
+// and that the user has joined.
+func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
teams := make([]*Team, 0, org.NumTeams)
return teams, db.GetEngine(ctx).
Where("`team_user`.org_id = ?", org.ID).
@@ -616,7 +582,8 @@ func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ..
Find(&teams)
}
-func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
+// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
+func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
teamIDs := make([]int64, 0, org.NumTeams)
return teamIDs, db.GetEngine(ctx).
Table("team").
@@ -635,180 +602,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
"team_user.uid": userID,
})
}
-
-// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
-func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
- return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
-}
-
-// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
-func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
- return org.getUserTeamIDs(ctx, userID)
-}
-
-// GetUserTeams returns all teams that belong to user,
-// and that the user has joined.
-func (org *Organization) GetUserTeams(ctx context.Context, userID int64) ([]*Team, error) {
- return org.getUserTeams(ctx, userID)
-}
-
-// AccessibleReposEnvironment operations involving the repositories that are
-// accessible to a particular user
-type AccessibleReposEnvironment interface {
- CountRepos() (int64, error)
- RepoIDs(page, pageSize int) ([]int64, error)
- Repos(page, pageSize int) (repo_model.RepositoryList, error)
- MirrorRepos() (repo_model.RepositoryList, error)
- AddKeyword(keyword string)
- SetSort(db.SearchOrderBy)
-}
-
-type accessibleReposEnv struct {
- org *Organization
- user *user_model.User
- team *Team
- teamIDs []int64
- ctx context.Context
- keyword string
- orderBy db.SearchOrderBy
-}
-
-// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
-// that are accessible to the specified user.
-func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) {
- var user *user_model.User
-
- if userID > 0 {
- u, err := user_model.GetUserByID(ctx, userID)
- if err != nil {
- return nil, err
- }
- user = u
- }
-
- teamIDs, err := org.getUserTeamIDs(ctx, userID)
- if err != nil {
- return nil, err
- }
- return &accessibleReposEnv{
- org: org,
- user: user,
- teamIDs: teamIDs,
- ctx: ctx,
- orderBy: db.SearchOrderByRecentUpdated,
- }, nil
-}
-
-// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
-// that are accessible to the specified team.
-func (org *Organization) AccessibleTeamReposEnv(ctx context.Context, team *Team) AccessibleReposEnvironment {
- return &accessibleReposEnv{
- org: org,
- team: team,
- ctx: ctx,
- orderBy: db.SearchOrderByRecentUpdated,
- }
-}
-
-func (env *accessibleReposEnv) cond() builder.Cond {
- cond := builder.NewCond()
- if env.team != nil {
- cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
- } else {
- if env.user == nil || !env.user.IsRestricted {
- cond = cond.Or(builder.Eq{
- "`repository`.owner_id": env.org.ID,
- "`repository`.is_private": false,
- })
- }
- if len(env.teamIDs) > 0 {
- cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
- }
- }
- if env.keyword != "" {
- cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
- }
- return cond
-}
-
-func (env *accessibleReposEnv) CountRepos() (int64, error) {
- repoCount, err := db.GetEngine(env.ctx).
- Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
- Where(env.cond()).
- Distinct("`repository`.id").
- Count(&repo_model.Repository{})
- if err != nil {
- return 0, fmt.Errorf("count user repositories in organization: %w", err)
- }
- return repoCount, nil
-}
-
-func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
- if page <= 0 {
- page = 1
- }
-
- repoIDs := make([]int64, 0, pageSize)
- return repoIDs, db.GetEngine(env.ctx).
- Table("repository").
- Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
- Where(env.cond()).
- GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
- OrderBy(string(env.orderBy)).
- Limit(pageSize, (page-1)*pageSize).
- Cols("`repository`.id").
- Find(&repoIDs)
-}
-
-func (env *accessibleReposEnv) Repos(page, pageSize int) (repo_model.RepositoryList, error) {
- repoIDs, err := env.RepoIDs(page, pageSize)
- if err != nil {
- return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
- }
-
- repos := make([]*repo_model.Repository, 0, len(repoIDs))
- if len(repoIDs) == 0 {
- return repos, nil
- }
-
- return repos, db.GetEngine(env.ctx).
- In("`repository`.id", repoIDs).
- OrderBy(string(env.orderBy)).
- Find(&repos)
-}
-
-func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
- repoIDs := make([]int64, 0, 10)
- return repoIDs, db.GetEngine(env.ctx).
- Table("repository").
- Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
- Where(env.cond()).
- GroupBy("`repository`.id, `repository`.updated_unix").
- OrderBy(string(env.orderBy)).
- Cols("`repository`.id").
- Find(&repoIDs)
-}
-
-func (env *accessibleReposEnv) MirrorRepos() (repo_model.RepositoryList, error) {
- repoIDs, err := env.MirrorRepoIDs()
- if err != nil {
- return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
- }
-
- repos := make([]*repo_model.Repository, 0, len(repoIDs))
- if len(repoIDs) == 0 {
- return repos, nil
- }
-
- return repos, db.GetEngine(env.ctx).
- In("`repository`.id", repoIDs).
- Find(&repos)
-}
-
-func (env *accessibleReposEnv) AddKeyword(keyword string) {
- env.keyword = keyword
-}
-
-func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
- env.orderBy = orderBy
-}
diff --git a/models/organization/org_list.go b/models/organization/org_list.go
index 4c4168af1f..81457191fe 100644
--- a/models/organization/org_list.go
+++ b/models/organization/org_list.go
@@ -50,8 +50,8 @@ type SearchOrganizationsOptions struct {
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
- UserID int64
- IncludePrivate bool
+ UserID int64
+ IncludeVisibility structs.VisibleType
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
@@ -65,11 +65,10 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
- cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
- }
- if !opts.IncludePrivate {
- cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
+ cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludeVisibility == structs.VisibleTypePrivate)))
}
+ // public=0, limited=1, private=2
+ cond = cond.And(builder.Lte{"`user`.visibility": opts.IncludeVisibility})
return cond
}
@@ -77,6 +76,16 @@ func (opts FindOrgOptions) ToOrders() string {
return "`user`.lower_name ASC"
}
+func DoerViewOtherVisibility(doer, other *user_model.User) structs.VisibleType {
+ if doer == nil || other == nil {
+ return structs.VisibleTypePublic
+ }
+ if doer.IsAdmin || doer.ID == other.ID {
+ return structs.VisibleTypePrivate
+ }
+ return structs.VisibleTypeLimited
+}
+
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
// are allowed to create repos.
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
@@ -124,6 +133,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg,
if err := db.GetEngine(ctx).Select(columnsStr).
Table("user").
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))).
+ OrderBy("`user`.lower_name ASC").
Find(&orgs); err != nil {
return nil, err
}
diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go
index 0f0f8a4bcd..a2a25c6f91 100644
--- a/models/organization/org_list_test.go
+++ b/models/organization/org_list_test.go
@@ -10,25 +10,32 @@ import (
"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/structs"
"github.com/stretchr/testify/assert"
)
-func TestCountOrganizations(t *testing.T) {
+func TestOrgList(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
+ t.Run("CountOrganizations", testCountOrganizations)
+ t.Run("FindOrgs", testFindOrgs)
+ t.Run("GetUserOrgsList", testGetUserOrgsList)
+ t.Run("LoadOrgListTeams", testLoadOrgListTeams)
+ t.Run("DoerViewOtherVisibility", testDoerViewOtherVisibility)
+}
+
+func testCountOrganizations(t *testing.T) {
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
assert.NoError(t, err)
- cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
+ cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludeVisibility: structs.VisibleTypePrivate})
assert.NoError(t, err)
assert.Equal(t, expected, cnt)
}
-func TestFindOrgs(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
+func testFindOrgs(t *testing.T) {
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: true,
+ UserID: 4,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
@@ -36,33 +43,30 @@ func TestFindOrgs(t *testing.T) {
}
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: false,
+ UserID: 4,
})
assert.NoError(t, err)
assert.Empty(t, orgs)
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: true,
+ UserID: 4,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
}
-func TestGetUserOrgsList(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
+func testGetUserOrgsList(t *testing.T) {
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID)
// repo_id: 3 is in the team, 32 is public, 5 is private with no team
- assert.EqualValues(t, 2, orgs[0].NumRepos)
+ assert.Equal(t, 2, orgs[0].NumRepos)
}
}
-func TestLoadOrgListTeams(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
+func testLoadOrgListTeams(t *testing.T) {
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
assert.NoError(t, err)
assert.Len(t, orgs, 1)
@@ -71,3 +75,10 @@ func TestLoadOrgListTeams(t *testing.T) {
assert.Len(t, teamsMap, 1)
assert.Len(t, teamsMap[3], 5)
}
+
+func testDoerViewOtherVisibility(t *testing.T) {
+ assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(nil, nil))
+ assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 2}))
+ assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 1}))
+ assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1, IsAdmin: true}, &user_model.User{ID: 2}))
+}
diff --git a/models/organization/org_repo.go b/models/organization/org_repo.go
deleted file mode 100644
index f7e59928f4..0000000000
--- a/models/organization/org_repo.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package organization
-
-import (
- "context"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
-)
-
-// GetOrgRepositories get repos belonging to the given organization
-func GetOrgRepositories(ctx context.Context, orgID int64) (repo_model.RepositoryList, error) {
- var orgRepos []*repo_model.Repository
- return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
-}
diff --git a/models/organization/org_test.go b/models/organization/org_test.go
index 5e99e88689..234325a8cd 100644
--- a/models/organization/org_test.go
+++ b/models/organization/org_test.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestUser_IsOwnedBy(t *testing.T) {
@@ -134,7 +135,7 @@ func TestIsOrganizationOwner(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isOwner, err := organization.IsOrganizationOwner(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isOwner)
+ assert.Equal(t, expected, isOwner)
}
test(3, 2, true)
test(3, 3, false)
@@ -148,7 +149,7 @@ func TestIsOrganizationMember(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isMember, err := organization.IsOrganizationMember(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isMember)
+ assert.Equal(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -163,7 +164,7 @@ func TestIsPublicMembership(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isMember, err := organization.IsPublicMembership(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isMember)
+ assert.Equal(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -180,9 +181,8 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
ID: 29,
IsRestricted: true,
})
- if !assert.True(t, restrictedUser.IsRestricted) {
- return // ensure fixtures return restricted user
- }
+ // ensure fixtures return restricted user
+ require.True(t, restrictedUser.IsRestricted)
testCases := []struct {
name string
@@ -237,7 +237,7 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
memberUIDs = append(memberUIDs, member.UID)
}
slices.Sort(memberUIDs)
- assert.EqualValues(t, tc.expectedUIDs, memberUIDs)
+ assert.Equal(t, tc.expectedUIDs, memberUIDs)
})
}
}
@@ -255,7 +255,7 @@ func TestGetOrgUsersByOrgID(t *testing.T) {
sort.Slice(orgUsers, func(i, j int) bool {
return orgUsers[i].ID < orgUsers[j].ID
})
- assert.EqualValues(t, []*organization.OrgUser{{
+ assert.Equal(t, []*organization.OrgUser{{
ID: 1,
OrgID: 3,
UID: 2,
@@ -318,11 +318,11 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID, expectedCount int64) {
- env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- count, err := env.CountRepos()
+ count, err := env.CountRepos(db.DefaultContext)
assert.NoError(t, err)
- assert.EqualValues(t, expectedCount, count)
+ assert.Equal(t, expectedCount, count)
}
testSuccess(2, 3)
testSuccess(4, 2)
@@ -332,9 +332,9 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
- env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repoIDs, err := env.RepoIDs(1, 100)
+ repoIDs, err := env.RepoIDs(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, expectedRepoIDs, repoIDs)
}
@@ -342,32 +342,13 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
testSuccess(4, []int64{3, 32})
}
-func TestAccessibleReposEnv_Repos(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
- testSuccess := func(userID int64, expectedRepoIDs []int64) {
- env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
- assert.NoError(t, err)
- repos, err := env.Repos(1, 100)
- assert.NoError(t, err)
- expectedRepos := make(repo_model.RepositoryList, len(expectedRepoIDs))
- for i, repoID := range expectedRepoIDs {
- expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
- &repo_model.Repository{ID: repoID})
- }
- assert.Equal(t, expectedRepos, repos)
- }
- testSuccess(2, []int64{3, 5, 32})
- testSuccess(4, []int64{3, 32})
-}
-
func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
- env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repos, err := env.MirrorRepos()
+ repos, err := env.MirrorRepos(db.DefaultContext)
assert.NoError(t, err)
expectedRepos := make(repo_model.RepositoryList, len(expectedRepoIDs))
for i, repoID := range expectedRepoIDs {
diff --git a/models/organization/org_user.go b/models/organization/org_user.go
index 1d3b2fab44..4d7527c15f 100644
--- a/models/organization/org_user.go
+++ b/models/organization/org_user.go
@@ -36,6 +36,21 @@ func init() {
db.RegisterModel(new(OrgUser))
}
+// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
+type ErrUserHasOrgs struct {
+ UID int64
+}
+
+// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
+func IsErrUserHasOrgs(err error) bool {
+ _, ok := err.(ErrUserHasOrgs)
+ return ok
+}
+
+func (err ErrUserHasOrgs) Error() string {
+ return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
+}
+
// GetOrganizationCount returns count of membership of organization of the user.
func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
return db.GetEngine(ctx).
@@ -63,7 +78,7 @@ func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
return false, err
}
for _, t := range teams {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return true, nil
}
}
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
index 55abb63203..689544430d 100644
--- a/models/organization/org_user_test.go
+++ b/models/organization/org_user_test.go
@@ -131,7 +131,7 @@ func TestAddOrgUser(t *testing.T) {
testSuccess := func(orgID, userID int64, isPublic bool) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
expectedNumMembers := org.NumMembers
- if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
+ if unittest.GetBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}) == nil {
expectedNumMembers++
}
assert.NoError(t, organization.AddOrgUser(db.DefaultContext, orgID, userID))
@@ -139,7 +139,7 @@ func TestAddOrgUser(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, ou)
assert.Equal(t, isPublic, ou.IsPublic)
org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
- assert.EqualValues(t, expectedNumMembers, org.NumMembers)
+ assert.Equal(t, expectedNumMembers, org.NumMembers)
}
setting.Service.DefaultOrgMemberVisible = false
diff --git a/models/organization/org_worktime.go b/models/organization/org_worktime.go
new file mode 100644
index 0000000000..7b57182a8a
--- /dev/null
+++ b/models/organization/org_worktime.go
@@ -0,0 +1,103 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package organization
+
+import (
+ "sort"
+
+ "code.gitea.io/gitea/models/db"
+
+ "xorm.io/builder"
+)
+
+type WorktimeSumByRepos struct {
+ RepoName string
+ SumTime int64
+}
+
+func GetWorktimeByRepos(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByRepos, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("repository.name AS repo_name, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("repository.name").
+ OrderBy("repository.name").
+ Find(&results)
+ return results, err
+}
+
+type WorktimeSumByMilestones struct {
+ RepoName string
+ MilestoneName string
+ MilestoneID int64
+ MilestoneDeadline int64
+ SumTime int64
+ HideRepoName bool
+}
+
+func GetWorktimeByMilestones(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMilestones, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("repository.name AS repo_name, milestone.name AS milestone_name, milestone.id AS milestone_id, milestone.deadline_unix as milestone_deadline, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("repository.name, milestone.name, milestone.deadline_unix, milestone.id").
+ OrderBy("repository.name, milestone.deadline_unix, milestone.id").
+ Find(&results)
+
+ // TODO: pgsql: NULL values are sorted last in default ascending order, so we need to sort them manually again.
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].RepoName != results[j].RepoName {
+ return results[i].RepoName < results[j].RepoName
+ }
+ if results[i].MilestoneDeadline != results[j].MilestoneDeadline {
+ return results[i].MilestoneDeadline < results[j].MilestoneDeadline
+ }
+ return results[i].MilestoneID < results[j].MilestoneID
+ })
+
+ // Show only the first RepoName, for nicer output.
+ prevRepoName := ""
+ for i := 0; i < len(results); i++ {
+ res := &results[i]
+ res.MilestoneDeadline = 0 // clear the deadline because we do not really need it
+ if prevRepoName == res.RepoName {
+ res.HideRepoName = true
+ }
+ prevRepoName = res.RepoName
+ }
+ return results, err
+}
+
+type WorktimeSumByMembers struct {
+ UserName string
+ SumTime int64
+}
+
+func GetWorktimeByMembers(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMembers, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("`user`.name AS user_name, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Join("INNER", "`user`", "tracked_time.user_id = `user`.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("`user`.name").
+ OrderBy("sum_time DESC").
+ Find(&results)
+ return results, err
+}
diff --git a/models/organization/team.go b/models/organization/team.go
index fb7f0c0493..7f3a9b3829 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -78,9 +77,8 @@ type Team struct {
LowerName string
Name string
Description string
- AccessMode perm.AccessMode `xorm:"'authorize'"`
- Repos []*repo_model.Repository `xorm:"-"`
- Members []*user_model.User `xorm:"-"`
+ AccessMode perm.AccessMode `xorm:"'authorize'"`
+ Members []*user_model.User `xorm:"-"`
NumRepos int
NumMembers int
Units []*TeamUnit `xorm:"-"`
@@ -115,7 +113,7 @@ func (t *Team) LoadUnits(ctx context.Context) (err error) {
// GetUnitNames returns the team units names
func (t *Team) GetUnitNames() (res []string) {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return unit.AllUnitKeyNames()
}
@@ -128,7 +126,7 @@ func (t *Team) GetUnitNames() (res []string) {
// GetUnitsMap returns the team units permissions
func (t *Team) GetUnitsMap() map[string]string {
m := make(map[string]string)
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
for _, u := range unit.Units {
m[u.NameKey] = t.AccessMode.ToString()
}
@@ -155,15 +153,8 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool {
return isMember
}
-// LoadRepositories returns paginated repositories in team of organization.
-func (t *Team) LoadRepositories(ctx context.Context) (err error) {
- if t.Repos != nil {
- return nil
- }
- t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{
- TeamID: t.ID,
- })
- return err
+func (t *Team) HasAdminAccess() bool {
+ return t.AccessMode >= perm.AccessModeAdmin
}
// LoadMembers returns paginated members in team of organization.
@@ -251,22 +242,6 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
return t, nil
}
-// GetTeamNamesByID returns team's lower name from a list of team ids.
-func GetTeamNamesByID(ctx context.Context, teamIDs []int64) ([]string, error) {
- if len(teamIDs) == 0 {
- return []string{}, nil
- }
-
- var teamNames []string
- err := db.GetEngine(ctx).Table("team").
- Select("lower_name").
- In("id", teamIDs).
- Asc("name").
- Find(&teamNames)
-
- return teamNames, err
-}
-
// IncrTeamRepoNum increases the number of repos for the given team by 1
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
diff --git a/models/organization/team_list.go b/models/organization/team_list.go
index 4ceb405e31..0274f9c5ba 100644
--- a/models/organization/team_list.go
+++ b/models/organization/team_list.go
@@ -9,7 +9,6 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"xorm.io/builder"
@@ -98,11 +97,11 @@ func SearchTeam(ctx context.Context, opts *SearchTeamOptions) (TeamList, int64,
}
// GetRepoTeams gets the list of teams that has access to the repository
-func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) {
+func GetRepoTeams(ctx context.Context, orgID, repoID int64) (teams TeamList, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
- Where("team.org_id = ?", repo.OwnerID).
- And("team_repo.repo_id=?", repo.ID).
+ Where("team.org_id = ?", orgID).
+ And("team_repo.repo_id=?", repoID).
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}
@@ -134,5 +133,8 @@ func GetTeamsByOrgIDs(ctx context.Context, orgIDs []int64) (TeamList, error) {
func GetTeamsByIDs(ctx context.Context, teamIDs []int64) (map[int64]*Team, error) {
teams := make(map[int64]*Team, len(teamIDs))
+ if len(teamIDs) == 0 {
+ return teams, nil
+ }
return teams, db.GetEngine(ctx).Where(builder.In("`id`", teamIDs)).Find(&teams)
}
diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go
index c90dfdeda0..b3e266dbc7 100644
--- a/models/organization/team_repo.go
+++ b/models/organization/team_repo.go
@@ -8,7 +8,6 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"xorm.io/builder"
@@ -32,29 +31,6 @@ func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool {
return has
}
-type SearchTeamRepoOptions struct {
- db.ListOptions
- TeamID int64
-}
-
-// GetRepositories returns paginated repositories in team of organization.
-func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (repo_model.RepositoryList, error) {
- sess := db.GetEngine(ctx)
- if opts.TeamID > 0 {
- sess = sess.In("id",
- builder.Select("repo_id").
- From("team_repo").
- Where(builder.Eq{"team_id": opts.TeamID}),
- )
- }
- if opts.PageSize > 0 {
- sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
- }
- var repos []*repo_model.Repository
- return repos, sess.OrderBy("repository.name").
- Find(&repos)
-}
-
// AddTeamRepo adds a repo for an organization's team
func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error {
_, err := db.GetEngine(ctx).Insert(&TeamRepo{
@@ -74,26 +50,27 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
return err
}
-// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
-func GetTeamsWithAccessToRepo(ctx context.Context, orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) {
+// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
+// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
+// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details
+func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) ([]*Team, error) {
teams := make([]*Team, 0, 5)
- return teams, db.GetEngine(ctx).Where("team.authorize >= ?", mode).
- Join("INNER", "team_repo", "team_repo.team_id = team.id").
- And("team_repo.org_id = ?", orgID).
- And("team_repo.repo_id = ?", repoID).
- OrderBy("name").
- Find(&teams)
-}
-// GetTeamsWithAccessToRepoUnit returns all teams in an organization that have given access level to the repository special unit.
-func GetTeamsWithAccessToRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) {
- teams := make([]*Team, 0, 5)
- return teams, db.GetEngine(ctx).Where("team_unit.access_mode >= ?", mode).
+ sub := builder.Select("team_id").From("team_unit").
+ Where(builder.Expr("team_unit.team_id = team.id")).
+ And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
+ And(builder.Expr("team_unit.access_mode >= ?", mode))
+
+ err := db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
- Join("INNER", "team_unit", "team_unit.team_id = team.id").
And("team_repo.org_id = ?", orgID).
And("team_repo.repo_id = ?", repoID).
- And("team_unit.type = ?", unitType).
+ And(builder.Or(
+ builder.Expr("team.authorize >= ?", mode),
+ builder.In("team.id", sub),
+ )).
OrderBy("name").
Find(&teams)
+
+ return teams, err
}
diff --git a/models/organization/team_repo_test.go b/models/organization/team_repo_test.go
index c0d6750df9..73a06a93c2 100644
--- a/models/organization/team_repo_test.go
+++ b/models/organization/team_repo_test.go
@@ -22,7 +22,7 @@ func TestGetTeamsWithAccessToRepoUnit(t *testing.T) {
org41 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 41})
repo61 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 61})
- teams, err := organization.GetTeamsWithAccessToRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
assert.NoError(t, err)
if assert.Len(t, teams, 2) {
assert.EqualValues(t, 21, teams[0].ID)
diff --git a/models/organization/team_test.go b/models/organization/team_test.go
index 8c34e7a612..b0bf842584 100644
--- a/models/organization/team_test.go
+++ b/models/organization/team_test.go
@@ -8,6 +8,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -42,9 +43,12 @@ func TestTeam_GetRepositories(t *testing.T) {
test := func(teamID int64) {
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
- assert.NoError(t, team.LoadRepositories(db.DefaultContext))
- assert.Len(t, team.Repos, team.NumRepos)
- for _, repo := range team.Repos {
+ repos, err := repo_model.GetTeamRepositories(db.DefaultContext, &repo_model.SearchTeamRepoOptions{
+ TeamID: team.ID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, repos, team.NumRepos)
+ for _, repo := range repos {
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repo.ID})
}
}
@@ -73,7 +77,7 @@ func TestGetTeam(t *testing.T) {
testSuccess := func(orgID int64, name string) {
team, err := organization.GetTeam(db.DefaultContext, orgID, name)
assert.NoError(t, err)
- assert.EqualValues(t, orgID, team.OrgID)
+ assert.Equal(t, orgID, team.OrgID)
assert.Equal(t, name, team.Name)
}
testSuccess(3, "Owners")
@@ -91,7 +95,7 @@ func TestGetTeamByID(t *testing.T) {
testSuccess := func(teamID int64) {
team, err := organization.GetTeamByID(db.DefaultContext, teamID)
assert.NoError(t, err)
- assert.EqualValues(t, teamID, team.ID)
+ assert.Equal(t, teamID, team.ID)
}
testSuccess(1)
testSuccess(2)
@@ -159,7 +163,7 @@ func TestGetUserOrgTeams(t *testing.T) {
teams, err := organization.GetUserOrgTeams(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
for _, team := range teams {
- assert.EqualValues(t, orgID, team.OrgID)
+ assert.Equal(t, orgID, team.OrgID)
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{TeamID: team.ID, UID: userID})
}
}
diff --git a/models/packages/container/search.go b/models/packages/container/search.go
index 5df35117ce..9321d9eb41 100644
--- a/models/packages/container/search.go
+++ b/models/packages/container/search.go
@@ -25,6 +25,7 @@ type BlobSearchOptions struct {
Digest string
Tag string
IsManifest bool
+ OnlyLead bool
Repository string
}
@@ -43,7 +44,10 @@ func (opts *BlobSearchOptions) toConds() builder.Cond {
cond = cond.And(builder.Eq{"package_version.lower_version": strings.ToLower(opts.Tag)})
}
if opts.IsManifest {
- cond = cond.And(builder.Eq{"package_file.lower_name": ManifestFilename})
+ cond = cond.And(builder.Eq{"package_file.lower_name": container_module.ManifestFilename})
+ }
+ if opts.OnlyLead {
+ cond = cond.And(builder.Eq{"package_file.is_lead": true})
}
if opts.Digest != "" {
var propsCond builder.Cond = builder.Eq{
@@ -73,11 +77,9 @@ func GetContainerBlob(ctx context.Context, opts *BlobSearchOptions) (*packages.P
pfds, err := getContainerBlobsLimit(ctx, opts, 1)
if err != nil {
return nil, err
- }
- if len(pfds) != 1 {
+ } else if len(pfds) == 0 {
return nil, ErrContainerBlobNotExist
}
-
return pfds[0], nil
}
@@ -233,7 +235,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
var cond builder.Cond = builder.Eq{
"package_version.is_internal": true,
- "package_version.lower_version": UploadVersion,
+ "package_version.lower_version": container_module.UploadVersion,
"package.type": packages.TypeContainer,
}
cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-olderThan).Unix()})
diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go
index 803b73c968..2d43dc3046 100644
--- a/models/packages/descriptor.go
+++ b/models/packages/descriptor.go
@@ -11,6 +11,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/packages/alpine"
"code.gitea.io/gitea/modules/packages/arch"
@@ -102,19 +103,26 @@ func (pd *PackageDescriptor) CalculateBlobSize() int64 {
// GetPackageDescriptor gets the package description for a version
func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDescriptor, error) {
- p, err := GetPackageByID(ctx, pv.PackageID)
+ return GetPackageDescriptorWithCache(ctx, pv, cache.NewEphemeralCache())
+}
+
+func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *cache.EphemeralCache) (*PackageDescriptor, error) {
+ p, err := cache.GetWithEphemeralCache(ctx, c, "package", pv.PackageID, GetPackageByID)
if err != nil {
return nil, err
}
- o, err := user_model.GetUserByID(ctx, p.OwnerID)
+ o, err := cache.GetWithEphemeralCache(ctx, c, "user", p.OwnerID, user_model.GetUserByID)
if err != nil {
return nil, err
}
- repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID)
- if err != nil && !repo_model.IsErrRepoNotExist(err) {
- return nil, err
+ var repository *repo_model.Repository
+ if p.RepoID > 0 {
+ repository, err = cache.GetWithEphemeralCache(ctx, c, "repo", p.RepoID, repo_model.GetRepositoryByID)
+ if err != nil && !repo_model.IsErrRepoNotExist(err) {
+ return nil, err
+ }
}
- creator, err := user_model.GetUserByID(ctx, pv.CreatorID)
+ creator, err := cache.GetWithEphemeralCache(ctx, c, "user", pv.CreatorID, user_model.GetUserByID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
creator = user_model.NewGhostUser()
@@ -142,9 +150,13 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err
}
- pfds, err := GetPackageFileDescriptors(ctx, pfs)
- if err != nil {
- return nil, err
+ pfds := make([]*PackageFileDescriptor, 0, len(pfs))
+ for _, pf := range pfs {
+ pfd, err := getPackageFileDescriptor(ctx, pf, c)
+ if err != nil {
+ return nil, err
+ }
+ pfds = append(pfds, pfd)
}
var metadata any
@@ -194,7 +206,7 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
case TypeVagrant:
metadata = &vagrant.Metadata{}
default:
- panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
+ panic("unknown package type: " + string(p.Type))
}
if metadata != nil {
if err := json.Unmarshal([]byte(pv.MetadataJSON), &metadata); err != nil {
@@ -218,7 +230,11 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
// GetPackageFileDescriptor gets a package file descriptor for a package file
func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFileDescriptor, error) {
- pb, err := GetBlobByID(ctx, pf.BlobID)
+ return getPackageFileDescriptor(ctx, pf, cache.NewEphemeralCache())
+}
+
+func getPackageFileDescriptor(ctx context.Context, pf *PackageFile, c *cache.EphemeralCache) (*PackageFileDescriptor, error) {
+ pb, err := cache.GetWithEphemeralCache(ctx, c, "package_file_blob", pf.BlobID, GetBlobByID)
if err != nil {
return nil, err
}
@@ -248,9 +264,13 @@ func GetPackageFileDescriptors(ctx context.Context, pfs []*PackageFile) ([]*Pack
// GetPackageDescriptors gets the package descriptions for the versions
func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*PackageDescriptor, error) {
+ return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
+}
+
+func getPackageDescriptors(ctx context.Context, pvs []*PackageVersion, c *cache.EphemeralCache) ([]*PackageDescriptor, error) {
pds := make([]*PackageDescriptor, 0, len(pvs))
for _, pv := range pvs {
- pd, err := GetPackageDescriptor(ctx, pv)
+ pd, err := GetPackageDescriptorWithCache(ctx, pv, c)
if err != nil {
return nil, err
}
diff --git a/models/packages/nuget/search.go b/models/packages/nuget/search.go
index 7a505ff08f..a4b23f31d5 100644
--- a/models/packages/nuget/search.go
+++ b/models/packages/nuget/search.go
@@ -33,7 +33,7 @@ func SearchVersions(ctx context.Context, opts *packages_model.PackageSearchOptio
Where(cond).
OrderBy("package.name ASC")
if opts.Paginator != nil {
- skip, take := opts.GetSkipTake()
+ skip, take := opts.Paginator.GetSkipTake()
inner = inner.Limit(take, skip)
}
diff --git a/models/packages/package.go b/models/packages/package.go
index 417d62d199..38d1cdcf66 100644
--- a/models/packages/package.go
+++ b/models/packages/package.go
@@ -127,7 +127,7 @@ func (pt Type) Name() string {
case TypeVagrant:
return "Vagrant"
}
- panic(fmt.Sprintf("unknown package type: %s", string(pt)))
+ panic("unknown package type: " + string(pt))
}
// SVGName gets the name of the package type svg image
@@ -178,7 +178,7 @@ func (pt Type) SVGName() string {
case TypeVagrant:
return "gitea-vagrant"
}
- panic(fmt.Sprintf("unknown package type: %s", string(pt)))
+ panic("unknown package type: " + string(pt))
}
// Package represents a package
@@ -228,6 +228,11 @@ func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
return err
}
+func UnlinkRepository(ctx context.Context, packageID int64) error {
+ _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: 0})
+ return err
+}
+
// UnlinkRepositoryFromAllPackages unlinks every package from the repository
func UnlinkRepositoryFromAllPackages(ctx context.Context, repoID int64) error {
_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Cols("repo_id").Update(&Package{})
@@ -248,6 +253,18 @@ func GetPackageByID(ctx context.Context, packageID int64) (*Package, error) {
return p, nil
}
+// UpdatePackageNameByID updates the package's name, it is only for internal usage, for example: rename some legacy packages
+func UpdatePackageNameByID(ctx context.Context, ownerID int64, packageType Type, packageID int64, name string) error {
+ var cond builder.Cond = builder.Eq{
+ "package.id": packageID,
+ "package.owner_id": ownerID,
+ "package.type": packageType,
+ "package.is_internal": false,
+ }
+ _, err := db.GetEngine(ctx).Where(cond).Update(&Package{Name: name, LowerName: strings.ToLower(name)})
+ return err
+}
+
// GetPackageByName gets a package by name
func GetPackageByName(ctx context.Context, ownerID int64, packageType Type, name string) (*Package, error) {
var cond builder.Cond = builder.Eq{
@@ -301,6 +318,21 @@ func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
Find(&ps)
}
+// ErrUserOwnPackages notifies that the user (still) owns the packages.
+type ErrUserOwnPackages struct {
+ UID int64
+}
+
+// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
+func IsErrUserOwnPackages(err error) bool {
+ _, ok := err.(ErrUserOwnPackages)
+ return ok
+}
+
+func (err ErrUserOwnPackages) Error() string {
+ return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
+}
+
// HasOwnerPackages tests if a user/org has accessible packages
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
return db.GetEngine(ctx).
diff --git a/models/packages/package_file.go b/models/packages/package_file.go
index 270cb32fdf..bf877485d6 100644
--- a/models/packages/package_file.go
+++ b/models/packages/package_file.go
@@ -115,6 +115,11 @@ func DeleteFileByID(ctx context.Context, fileID int64) error {
return err
}
+func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error {
+ _, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf)
+ return err
+}
+
// PackageFileSearchOptions are options for SearchXXX methods
type PackageFileSearchOptions struct {
OwnerID int64
diff --git a/models/packages/package_property.go b/models/packages/package_property.go
index e0170016cf..7ddbfd97e9 100644
--- a/models/packages/package_property.go
+++ b/models/packages/package_property.go
@@ -66,6 +66,20 @@ func UpdateProperty(ctx context.Context, pp *PackageProperty) error {
return err
}
+func InsertOrUpdateProperty(ctx context.Context, refType PropertyType, refID int64, name, value string) error {
+ pp := PackageProperty{RefType: refType, RefID: refID, Name: name}
+ ok, err := db.GetEngine(ctx).Get(&pp)
+ if err != nil {
+ return err
+ }
+ if ok {
+ _, err = db.GetEngine(ctx).Where("ref_type=? AND ref_id=? AND name=?", refType, refID, name).Cols("value").Update(&PackageProperty{Value: value})
+ return err
+ }
+ _, err = InsertProperty(ctx, refType, refID, name, value)
+ return err
+}
+
// DeleteAllProperties deletes all properties of a ref
func DeleteAllProperties(ctx context.Context, refType PropertyType, refID int64) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ?", refType, refID).Delete(&PackageProperty{})
@@ -78,8 +92,8 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
return err
}
-// DeletePropertyByName deletes properties by name
-func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
+// DeletePropertiesByName deletes properties by name
+func DeletePropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}
diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 278e8e3a86..5672e0efbf 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
+ "xorm.io/xorm"
)
// ErrDuplicatePackageVersion indicates a duplicated package version error
@@ -187,7 +188,7 @@ type PackageSearchOptions struct {
HasFileWithName string // only results are found which are associated with a file with the specific name
HasFiles optional.Option[bool] // only results are found which have associated files
Sort VersionSort
- db.Paginator
+ Paginator db.Paginator
}
func (opts *PackageSearchOptions) ToConds() builder.Cond {
@@ -279,9 +280,19 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
default:
e.Desc("package_version.created_unix")
}
+ e.Desc("package_version.id") // Sort by id for stable order with duplicates in the other field
+}
- // Sort by id for stable order with duplicates in the other field
- e.Asc("package_version.id")
+func searchVersionsBySession(sess *xorm.Session, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
+ opts.configureOrderBy(sess)
+ pvs := make([]*PackageVersion, 0, 10)
+ if opts.Paginator != nil {
+ sess = db.SetSessionPagination(sess, opts.Paginator)
+ count, err := sess.FindAndCount(&pvs)
+ return pvs, count, err
+ }
+ err := sess.Find(&pvs)
+ return pvs, int64(len(pvs)), err
}
// SearchVersions gets all versions of packages matching the search options
@@ -291,16 +302,7 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
-
- opts.configureOrderBy(sess)
-
- if opts.Paginator != nil {
- sess = db.SetSessionPagination(sess, opts)
- }
-
- pvs := make([]*PackageVersion, 0, 10)
- count, err := sess.FindAndCount(&pvs)
- return pvs, count, err
+ return searchVersionsBySession(sess, opts)
}
// SearchLatestVersions gets the latest version of every package matching the search options
@@ -318,15 +320,7 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
Join("INNER", "package", "package.id = package_version.package_id").
Where(builder.In("package_version.id", in))
- opts.configureOrderBy(sess)
-
- if opts.Paginator != nil {
- sess = db.SetSessionPagination(sess, opts)
- }
-
- pvs := make([]*PackageVersion, 0, 10)
- count, err := sess.FindAndCount(&pvs)
- return pvs, count, err
+ return searchVersionsBySession(sess, opts)
}
// ExistVersion checks if a version matching the search options exist
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 0ed116a132..7de43ecd07 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.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/log"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
@@ -25,7 +26,8 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode map[unit.Type]perm_model.AccessMode
- everyoneAccessMode map[unit.Type]perm_model.AccessMode
+ everyoneAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for every signed-in user
+ anonymousAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for anonymous (non-signed-in) user
}
// IsOwner returns true if current user is the owner of repository.
@@ -39,7 +41,8 @@ func (p *Permission) IsAdmin() bool {
}
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
-// It doesn't count the "everyone access mode".
+// It doesn't count the "public(anonymous/everyone) access mode".
+// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
@@ -49,13 +52,22 @@ func (p *Permission) HasAnyUnitAccess() bool {
return p.AccessMode >= perm_model.AccessModeRead
}
-func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
+func (p *Permission) HasAnyUnitPublicAccess() bool {
+ for _, v := range p.anonymousAccessMode {
+ if v >= perm_model.AccessModeRead {
+ return true
+ }
+ }
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
- return p.HasAnyUnitAccess()
+ return false
+}
+
+func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
+ return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@@ -73,14 +85,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
-// It also considers "everyone access mode"
+// It also considers "public (anonymous/everyone) access mode"
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map does not contain the access mode, return the default access mode if the unit exists
- unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
+ unitDefaultAccessMode := p.AccessMode
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
}
@@ -152,7 +166,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
}
func (p *Permission) LogString() string {
- format := ""
+ format += "\n\tanonymousAccessMode: %-v"
+ args = append(args, p.anonymousAccessMode)
+ format += "\n\teveryoneAccessMode: %-v"
+ args = append(args, p.everyoneAccessMode)
+ format += "\n\t]>"
return fmt.Sprintf(format, args...)
}
-func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
- if user == nil || user.ID <= 0 {
+func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
+ if setting.Repository.ForcePrivate {
return
}
+ if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
+ if *modeMap == nil {
+ *modeMap = make(map[unit.Type]perm_model.AccessMode)
+ }
+ (*modeMap)[unitType] = accessMode
+ }
+}
+
+func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
+ // apply public (anonymous) access permissions
for _, u := range perm.units {
- if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
- if perm.everyoneAccessMode == nil {
- perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
+ applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
+ }
+
+ if user == nil || user.ID <= 0 {
+ // for anonymous access, it could be:
+ // AccessMode is None or Read, units has repo units, unitModes is nil
+ return
+ }
+
+ // apply public (everyone) access permissions
+ for _, u := range perm.units {
+ applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
+ }
+
+ if perm.unitsMode == nil {
+ // if unitsMode is not set, then it means that the default p.AccessMode applies to all units
+ return
+ }
+
+ // remove no permission units
+ origPermUnits := perm.units
+ perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
+ for _, u := range origPermUnits {
+ shouldKeep := false
+ for t := range perm.unitsMode {
+ if shouldKeep = u.Type == t; shouldKeep {
+ break
}
- perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
+ }
+ for t := range perm.anonymousAccessMode {
+ if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
+ break
+ }
+ }
+ for t := range perm.everyoneAccessMode {
+ if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
+ break
+ }
+ }
+ if shouldKeep {
+ perm.units = append(perm.units, u)
}
}
}
@@ -193,11 +257,9 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
defer func() {
if err == nil {
- applyEveryoneRepoPermission(user, &perm)
- }
- if log.IsTrace() {
- log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
+ finalProcessRepoUnitPermission(user, &perm)
}
+ log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
}()
if err = repo.LoadUnits(ctx); err != nil {
@@ -206,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
perm.units = repo.Units
// anonymous user visit private repo.
- // TODO: anonymous user visit public unit of private repo???
if user == nil && repo.IsPrivate {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -225,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
// Prevent strangers from checking out public repo of private organization/users
- // Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
+ // Allow user if they are a collaborator of a repo within a private user or a private organization but not a member of the organization itself
+ // TODO: rename it to "IsOwnerVisibleToDoer"
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -243,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
- // plain user
+ // plain user TODO: this check should be replaced, only need to check collaborator access mode
perm.AccessMode, err = accessLevel(ctx, user, repo)
if err != nil {
return perm, err
@@ -253,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
+ // now: the owner is visible to doer, if the repo is public, then the min access mode is read
+ minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone)
+ perm.AccessMode = max(perm.AccessMode, minAccessMode)
+
+ // get units mode from teams
+ teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
+ if err != nil {
+ return perm, err
+ }
+ if len(teams) == 0 {
+ return perm, nil
+ }
+
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
// Collaborators on organization
@@ -262,15 +337,9 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
}
- // get units mode from teams
- teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
- if err != nil {
- return perm, err
- }
-
// if user in an owner team
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
perm.AccessMode = perm_model.AccessModeOwner
perm.unitsMode = nil
return perm, nil
@@ -278,29 +347,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
for _, u := range repo.Units {
- var found bool
for _, team := range teams {
+ unitAccessMode := minAccessMode
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
- perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
- found = true
- }
- }
-
- // for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
- if !found && !repo.IsPrivate && !user.IsRestricted {
- if _, ok := perm.unitsMode[u.Type]; !ok {
- perm.unitsMode[u.Type] = perm_model.AccessModeRead
- }
- }
- }
-
- // remove no permission units
- perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
- for t := range perm.unitsMode {
- for _, u := range repo.Units {
- if u.Type == t {
- perm.units = append(perm.units, u)
+ unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
}
+ perm.unitsMode[u.Type] = unitAccessMode
}
}
@@ -348,7 +400,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
}
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
return true, nil
}
}
@@ -357,13 +409,13 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access.
-func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
+func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint:revive // export stutter
return AccessLevelUnit(ctx, user, repo, unit.TypeCode)
}
// AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
// user does not have access.
-func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
+func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint:revive // export stutter
perm, err := GetUserRepoPermission(ctx, repo, user)
if err != nil {
return perm_model.AccessModeNone, err
@@ -471,3 +523,7 @@ func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u
return perm.CanRead(unitType)
}
+
+func PermissionNoAccess() Permission {
+ return Permission{AccessMode: perm_model.AccessModeNone}
+}
diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go
index 50070c4368..c8675b1ded 100644
--- a/models/perm/access/repo_permission_test.go
+++ b/models/perm/access/repo_permission_test.go
@@ -6,12 +6,16 @@ package access
import (
"testing"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
perm_model "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestHasAnyUnitAccess(t *testing.T) {
@@ -22,14 +26,21 @@ func TestHasAnyUnitAccess(t *testing.T) {
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.False(t, perm.HasAnyUnitAccess())
- assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+ assert.False(t, perm.HasAnyUnitAccessOrPublicAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
}
assert.False(t, perm.HasAnyUnitAccess())
- assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+ assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
+
+ perm = Permission{
+ units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
+ anonymousAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
+ }
+ assert.False(t, perm.HasAnyUnitAccess())
+ assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
perm = Permission{
AccessMode: perm_model.AccessModeRead,
@@ -43,14 +54,32 @@ func TestHasAnyUnitAccess(t *testing.T) {
assert.True(t, perm.HasAnyUnitAccess())
}
-func TestApplyEveryoneRepoPermission(t *testing.T) {
+func TestApplyPublicAccessRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(nil, &perm)
+ finalProcessRepoUnitPermission(nil, &perm)
+ assert.False(t, perm.CanRead(unit.TypeWiki))
+
+ perm = Permission{
+ AccessMode: perm_model.AccessModeNone,
+ units: []*repo_model.RepoUnit{
+ {Type: unit.TypeWiki, AnonymousAccessMode: perm_model.AccessModeRead},
+ },
+ }
+ finalProcessRepoUnitPermission(nil, &perm)
+ assert.True(t, perm.CanRead(unit.TypeWiki))
+
+ perm = Permission{
+ AccessMode: perm_model.AccessModeNone,
+ units: []*repo_model.RepoUnit{
+ {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
+ },
+ }
+ finalProcessRepoUnitPermission(&user_model.User{ID: 0}, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -59,16 +88,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
- assert.False(t, perm.CanRead(unit.TypeWiki))
-
- perm = Permission{
- AccessMode: perm_model.AccessModeNone,
- units: []*repo_model.RepoUnit{
- {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
- },
- }
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -77,20 +97,22 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
assert.True(t, perm.CanWrite(unit.TypeWiki))
perm = Permission{
units: []*repo_model.RepoUnit{
+ {Type: unit.TypeCode}, // will be removed
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
unitsMode: map[unit.Type]perm_model.AccessMode{
unit.TypeWiki: perm_model.AccessModeWrite,
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanWrite(unit.TypeWiki))
+ assert.Len(t, perm.units, 1)
}
func TestUnitAccessMode(t *testing.T) {
@@ -134,3 +156,45 @@ func TestUnitAccessMode(t *testing.T) {
}
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map")
}
+
+func TestGetUserRepoPermission(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ ctx := t.Context()
+ repo32 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) // org public repo
+ require.NoError(t, repo32.LoadOwner(ctx))
+ require.True(t, repo32.Owner.IsOrganization())
+
+ require.NoError(t, db.TruncateBeans(ctx, &organization.Team{}, &organization.TeamUser{}, &organization.TeamRepo{}, &organization.TeamUnit{}))
+ org := repo32.Owner
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ team := &organization.Team{OrgID: org.ID, LowerName: "test_team"}
+ require.NoError(t, db.Insert(ctx, team))
+
+ t.Run("DoerInTeamWithNoRepo", func(t *testing.T) {
+ require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID}))
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Nil(t, perm.unitsMode) // doer in the team, but has no access to the repo
+ })
+
+ require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: repo32.ID}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeNone}))
+ t.Run("DoerWithTeamUnitAccessNone", func(t *testing.T) {
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeCode])
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
+ })
+
+ require.NoError(t, db.TruncateBeans(ctx, &organization.TeamUnit{}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeWrite}))
+ t.Run("DoerWithTeamUnitAccessWrite", func(t *testing.T) {
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode])
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
+ })
+}
diff --git a/models/project/column.go b/models/project/column.go
index 222f448599..9b9d874997 100644
--- a/models/project/column.go
+++ b/models/project/column.go
@@ -48,6 +48,8 @@ type Column struct {
ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
+ NumIssues int64 `xorm:"-"`
+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
@@ -57,20 +59,6 @@ func (Column) TableName() string {
return "project_board" // TODO: the legacy table name should be project_column
}
-// NumIssues return counter of all issues assigned to the column
-func (c *Column) NumIssues(ctx context.Context) int {
- total, err := db.GetEngine(ctx).Table("project_issue").
- Where("project_id=?", c.ProjectID).
- And("project_board_id=?", c.ID).
- GroupBy("issue_id").
- Cols("issue_id").
- Count()
- if err != nil {
- return 0
- }
- return int(total)
-}
-
func (c *Column) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
issues := make([]*ProjectIssue, 0, 5)
if err := db.GetEngine(ctx).Where("project_id=?", c.ProjectID).
@@ -159,7 +147,7 @@ func NewColumn(ctx context.Context, column *Column) error {
return err
}
if res.ColumnCount >= maxProjectColumns {
- return fmt.Errorf("NewBoard: maximum number of columns reached")
+ return errors.New("NewBoard: maximum number of columns reached")
}
column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
_, err := db.GetEngine(ctx).Insert(column)
@@ -184,7 +172,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error {
}
if column.Default {
- return fmt.Errorf("deleteColumnByID: cannot delete default column")
+ return errors.New("deleteColumnByID: cannot delete default column")
}
// move all issues to the default column
@@ -192,7 +180,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error {
if err != nil {
return err
}
- defaultColumn, err := project.GetDefaultColumn(ctx)
+ defaultColumn, err := project.MustDefaultColumn(ctx)
if err != nil {
return err
}
@@ -257,8 +245,8 @@ func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) {
return columns, nil
}
-// GetDefaultColumn return default column and ensure only one exists
-func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) {
+// getDefaultColumn return default column and ensure only one exists
+func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
var column Column
has, err := db.GetEngine(ctx).
Where("project_id=? AND `default` = ?", p.ID, true).
@@ -270,6 +258,33 @@ func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) {
if has {
return &column, nil
}
+ return nil, ErrProjectColumnNotExist{ColumnID: 0}
+}
+
+// MustDefaultColumn returns the default column for a project.
+// If one exists, it is returned
+// If none exists, the first column will be elevated to the default column of this project
+func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
+ c, err := p.getDefaultColumn(ctx)
+ if err != nil && !IsErrProjectColumnNotExist(err) {
+ return nil, err
+ }
+ if c != nil {
+ return c, nil
+ }
+
+ var column Column
+ has, err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
+ if err != nil {
+ return nil, err
+ }
+ if has {
+ column.Default = true
+ if _, err := db.GetEngine(ctx).ID(column.ID).Cols("`default`").Update(&column); err != nil {
+ return nil, err
+ }
+ return &column, nil
+ }
// create a default column if none is found
column = Column{
@@ -321,6 +336,9 @@ func UpdateColumnSorting(ctx context.Context, cl ColumnList) error {
func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) {
columns := make([]*Column, 0, 5)
+ if len(columnsIDs) == 0 {
+ return columns, nil
+ }
if err := db.GetEngine(ctx).
Where("project_id =?", projectID).
In("id", columnsIDs).
diff --git a/models/project/column_test.go b/models/project/column_test.go
index 566667e45d..6a615090a5 100644
--- a/models/project/column_test.go
+++ b/models/project/column_test.go
@@ -20,19 +20,19 @@ func TestGetDefaultColumn(t *testing.T) {
assert.NoError(t, err)
// check if default column was added
- column, err := projectWithoutDefault.GetDefaultColumn(db.DefaultContext)
+ column, err := projectWithoutDefault.MustDefaultColumn(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, int64(5), column.ProjectID)
- assert.Equal(t, "Uncategorized", column.Title)
+ assert.Equal(t, "Done", column.Title)
projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6)
assert.NoError(t, err)
// check if multiple defaults were removed
- column, err = projectWithMultipleDefaults.GetDefaultColumn(db.DefaultContext)
+ column, err = projectWithMultipleDefaults.MustDefaultColumn(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, int64(6), column.ProjectID)
- assert.Equal(t, int64(9), column.ID)
+ assert.Equal(t, int64(9), column.ID) // there are 2 default columns in the test data, use the latest one
// set 8 as default column
assert.NoError(t, SetDefaultColumn(db.DefaultContext, column.ProjectID, 8))
@@ -97,9 +97,9 @@ func Test_MoveColumnsOnProject(t *testing.T) {
columnsAfter, err := project1.GetColumns(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, columnsAfter, 3)
- assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
- assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID)
- assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
+ assert.Equal(t, columns[1].ID, columnsAfter[0].ID)
+ assert.Equal(t, columns[2].ID, columnsAfter[1].ID)
+ assert.Equal(t, columns[0].ID, columnsAfter[2].ID)
}
func Test_NewColumn(t *testing.T) {
@@ -110,7 +110,7 @@ func Test_NewColumn(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, columns, 3)
- for i := 0; i < maxProjectColumns-3; i++ {
+ for i := range maxProjectColumns - 3 {
err := NewColumn(db.DefaultContext, &Column{
Title: fmt.Sprintf("column-%d", i+4),
ProjectID: project1.ID,
diff --git a/models/project/issue.go b/models/project/issue.go
index b4347a9c2b..47d1537ec7 100644
--- a/models/project/issue.go
+++ b/models/project/issue.go
@@ -5,10 +5,9 @@ package project
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
@@ -34,51 +33,9 @@ func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error
return err
}
-// NumIssues return counter of all issues assigned to a project
-func (p *Project) NumIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Where("project_id=?", p.ID).
- GroupBy("issue_id").
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
-// NumClosedIssues return counter of closed issues assigned to a project
-func (p *Project) NumClosedIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Join("INNER", "issue", "project_issue.issue_id=issue.id").
- Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, true).
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumClosedIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
-// NumOpenIssues return counter of open issues assigned to a project
-func (p *Project) NumOpenIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Join("INNER", "issue", "project_issue.issue_id=issue.id").
- Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumOpenIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error {
if c.ProjectID != newColumn.ProjectID {
- return fmt.Errorf("columns have to be in the same project")
+ return errors.New("columns have to be in the same project")
}
if c.ID == newColumn.ID {
diff --git a/models/project/project.go b/models/project/project.go
index 9779908b9d..f516466854 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -97,6 +97,9 @@ type Project struct {
Type Type
RenderedContent template.HTML `xorm:"-"`
+ NumOpenIssues int64 `xorm:"-"`
+ NumClosedIssues int64 `xorm:"-"`
+ NumIssues int64 `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -126,6 +129,14 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) {
return err
}
+func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint:revive // export stutter
+ return fmt.Sprintf("%s/-/projects/%d", org.HomeLink(), projectID)
+}
+
+func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint:revive // export stutter
+ return fmt.Sprintf("%s/projects/%d", repo.Link(), projectID)
+}
+
// Link returns the project's relative URL.
func (p *Project) Link(ctx context.Context) string {
if p.OwnerID > 0 {
@@ -134,7 +145,7 @@ func (p *Project) Link(ctx context.Context) string {
log.Error("LoadOwner: %v", err)
return ""
}
- return fmt.Sprintf("%s/-/projects/%d", p.Owner.HomeLink(), p.ID)
+ return ProjectLinkForOrg(p.Owner, p.ID)
}
if p.RepoID > 0 {
err := p.LoadRepo(ctx)
@@ -142,7 +153,7 @@ func (p *Project) Link(ctx context.Context) string {
log.Error("LoadRepo: %v", err)
return ""
}
- return fmt.Sprintf("%s/projects/%d", p.Repo.Link(), p.ID)
+ return ProjectLinkForRepo(p.Repo, p.ID)
}
return ""
}
@@ -236,6 +247,10 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
return db.SearchOrderByRecentUpdated
case "leastupdate":
return db.SearchOrderByLeastUpdated
+ case "alphabetically":
+ return "title ASC"
+ case "reversealphabetically":
+ return "title DESC"
default:
return db.SearchOrderByNewest
}
@@ -256,7 +271,7 @@ func NewProject(ctx context.Context, p *Project) error {
return util.NewInvalidArgumentErrorf("project type is not valid")
}
- p.Title, _ = util.SplitStringAtByteN(p.Title, 255)
+ p.Title = util.EllipsisDisplayString(p.Title, 255)
return db.WithTx(ctx, func(ctx context.Context) error {
if err := db.Insert(ctx, p); err != nil {
@@ -311,7 +326,7 @@ func UpdateProject(ctx context.Context, p *Project) error {
p.CardType = CardTypeTextOnly
}
- p.Title, _ = util.SplitStringAtByteN(p.Title, 255)
+ p.Title = util.EllipsisDisplayString(p.Title, 255)
_, err := db.GetEngine(ctx).ID(p.ID).Cols(
"title",
"description",
diff --git a/models/project/project_test.go b/models/project/project_test.go
index dd421b4659..c2e924e8ae 100644
--- a/models/project/project_test.go
+++ b/models/project/project_test.go
@@ -113,10 +113,10 @@ func TestProjectsSort(t *testing.T) {
OrderBy: GetSearchOrderByBySortType(tt.sortType),
})
assert.NoError(t, err)
- assert.EqualValues(t, int64(6), count)
+ assert.Equal(t, int64(6), count)
if assert.Len(t, projects, 6) {
for i := range projects {
- assert.EqualValues(t, tt.wants[i], projects[i].ID)
+ assert.Equal(t, tt.wants[i], projects[i].ID)
}
}
}
diff --git a/models/pull/automerge.go b/models/pull/automerge.go
index f69fcb60d1..3cafacc3a4 100644
--- a/models/pull/automerge.go
+++ b/models/pull/automerge.go
@@ -15,13 +15,14 @@ import (
// AutoMerge represents a pull request scheduled for merging when checks succeed
type AutoMerge struct {
- ID int64 `xorm:"pk autoincr"`
- PullID int64 `xorm:"UNIQUE"`
- DoerID int64 `xorm:"INDEX NOT NULL"`
- Doer *user_model.User `xorm:"-"`
- MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
- Message string `xorm:"LONGTEXT"`
- CreatedUnix timeutil.TimeStamp `xorm:"created"`
+ ID int64 `xorm:"pk autoincr"`
+ PullID int64 `xorm:"UNIQUE"`
+ DoerID int64 `xorm:"INDEX NOT NULL"`
+ Doer *user_model.User `xorm:"-"`
+ MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
+ Message string `xorm:"LONGTEXT"`
+ DeleteBranchAfterMerge bool
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName return database table name for xorm
@@ -49,7 +50,7 @@ func IsErrAlreadyScheduledToAutoMerge(err error) bool {
}
// ScheduleAutoMerge schedules a pull request to be merged when all checks succeed
-func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string) error {
+func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) error {
// Check if we already have a merge scheduled for that pull request
if exists, _, err := GetScheduledMergeByPullID(ctx, pullID); err != nil {
return err
@@ -58,10 +59,11 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
}
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
- DoerID: doer.ID,
- PullID: pullID,
- MergeStyle: style,
- Message: message,
+ DoerID: doer.ID,
+ PullID: pullID,
+ MergeStyle: style,
+ Message: message,
+ DeleteBranchAfterMerge: deleteBranchAfterMerge,
})
return err
}
diff --git a/models/pull/review_state.go b/models/pull/review_state.go
index e46a22a49d..137af00eab 100644
--- a/models/pull/review_state.go
+++ b/models/pull/review_state.go
@@ -6,6 +6,7 @@ package pull
import (
"context"
"fmt"
+ "maps"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -100,9 +101,7 @@ func mergeFiles(oldFiles, newFiles map[string]ViewedState) map[string]ViewedStat
return oldFiles
}
- for file, viewed := range newFiles {
- oldFiles[file] = viewed
- }
+ maps.Copy(oldFiles, newFiles)
return oldFiles
}
diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go
index 4815643e67..407e45fb54 100644
--- a/models/renderhelper/commit_checker.go
+++ b/models/renderhelper/commit_checker.go
@@ -47,7 +47,7 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
c.gitRepo, c.gitRepoCloser = r, closer
}
- exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
+ exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashes with gogit edition.
c.commitCache[commitID] = exist
return exist
}
diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go
index 6bd5e91ad1..ae0fbf0abd 100644
--- a/models/renderhelper/repo_comment.go
+++ b/models/renderhelper/repo_comment.go
@@ -28,14 +28,14 @@ func (r *RepoComment) IsCommitIDExisting(commitID string) bool {
return r.commitChecker.IsCommitIDExisting(commitID)
}
-func (r *RepoComment) ResolveLink(link string, likeType markup.LinkType) (finalLink string) {
- switch likeType {
- case markup.LinkTypeApp:
- finalLink = r.ctx.ResolveLinkApp(link)
+func (r *RepoComment) ResolveLink(link, preferLinkType string) string {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ return r.ctx.ResolveLinkRoot(link)
default:
- finalLink = r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
+ return r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
}
- return finalLink
}
var _ markup.RenderHelper = (*RepoComment)(nil)
@@ -44,30 +44,31 @@ type RepoCommentOptions struct {
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
CurrentRefPath string // eg: "branch/main" or "commit/11223344"
+ FootnoteContextID string // the extra context ID for footnotes, used to avoid conflicts with other footnotes in the same page
}
func NewRenderContextRepoComment(ctx context.Context, repo *repo_model.Repository, opts ...RepoCommentOptions) *markup.RenderContext {
- helper := &RepoComment{
- repoLink: repo.Link(),
- opts: util.OptionalArg(opts),
- }
+ helper := &RepoComment{opts: util.OptionalArg(opts)}
rctx := markup.NewRenderContext(ctx)
helper.ctx = rctx
+ var metas map[string]string
if repo != nil {
helper.repoLink = repo.Link()
helper.commitChecker = newCommitChecker(ctx, repo)
- rctx = rctx.WithMetas(repo.ComposeMetas(ctx))
+ metas = repo.ComposeCommentMetas(ctx)
} else {
- // this is almost dead code, only to pass the incorrect tests
- helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
- rctx = rctx.WithMetas(map[string]string{
- "user": helper.opts.DeprecatedOwnerName,
- "repo": helper.opts.DeprecatedRepoName,
-
- "markdownLineBreakStyle": "comment",
- "markupAllowShortIssuePattern": "true",
- })
+ // repo can be nil when rendering a commit message in user's dashboard feedback whose repository has been deleted
+ metas = map[string]string{}
+ if helper.opts.DeprecatedOwnerName != "" {
+ // this is almost dead code, only to pass the incorrect tests
+ helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
+ metas["user"] = helper.opts.DeprecatedOwnerName
+ metas["repo"] = helper.opts.DeprecatedRepoName
+ }
+ metas["markdownNewLineHardBreak"] = "true"
+ metas["markupAllowShortIssuePattern"] = "true"
}
- rctx = rctx.WithHelper(helper)
+ metas["footnoteContextId"] = helper.opts.FootnoteContextID
+ rctx = rctx.WithMetas(metas).WithHelper(helper)
return rctx
}
diff --git a/models/renderhelper/repo_comment_test.go b/models/renderhelper/repo_comment_test.go
index 01e20b9e02..3b13bff73c 100644
--- a/models/renderhelper/repo_comment_test.go
+++ b/models/renderhelper/repo_comment_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -21,7 +20,7 @@ func TestRepoComment(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -36,7 +35,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
// It is Gitea's old behavior, the relative path is resolved to the repo path
// It is different from GitHub, GitHub resolves relative links to current page's path
@@ -56,7 +55,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoComment(t.Context(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
WithMarkupType(markdown.MarkupName)
// the ref path is only used to render commit message: a commit message is rendered at the commit page with its commit ID path
@@ -73,4 +72,11 @@ func TestRepoComment(t *testing.T) {

`, rendered)
})
+
+ t.Run("NoRepo", func(t *testing.T) {
+ rctx := NewRenderContextRepoComment(t.Context(), nil).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, "any")
+ assert.NoError(t, err)
+ assert.Equal(t, "any
\n", rendered)
+ })
}
diff --git a/models/renderhelper/repo_file.go b/models/renderhelper/repo_file.go
index 794828c617..e0375ed280 100644
--- a/models/renderhelper/repo_file.go
+++ b/models/renderhelper/repo_file.go
@@ -29,17 +29,17 @@ func (r *RepoFile) IsCommitIDExisting(commitID string) bool {
return r.commitChecker.IsCommitIDExisting(commitID)
}
-func (r *RepoFile) ResolveLink(link string, likeType markup.LinkType) string {
- finalLink := link
- switch likeType {
- case markup.LinkTypeApp:
- finalLink = r.ctx.ResolveLinkApp(link)
- case markup.LinkTypeDefault:
- finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+func (r *RepoFile) ResolveLink(link, preferLinkType string) (finalLink string) {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ finalLink = r.ctx.ResolveLinkRoot(link)
case markup.LinkTypeRaw:
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
case markup.LinkTypeMedia:
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+ default:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
}
return finalLink
}
@@ -61,15 +61,13 @@ func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository,
if repo != nil {
helper.repoLink = repo.Link()
helper.commitChecker = newCommitChecker(ctx, repo)
- rctx = rctx.WithMetas(repo.ComposeDocumentMetas(ctx))
+ rctx = rctx.WithMetas(repo.ComposeRepoFileMetas(ctx))
} else {
// this is almost dead code, only to pass the incorrect tests
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
rctx = rctx.WithMetas(map[string]string{
"user": helper.opts.DeprecatedOwnerName,
"repo": helper.opts.DeprecatedRepoName,
-
- "markdownLineBreakStyle": "document",
})
}
rctx = rctx.WithHelper(helper)
diff --git a/models/renderhelper/repo_file_test.go b/models/renderhelper/repo_file_test.go
index 959648b660..3b48efba3a 100644
--- a/models/renderhelper/repo_file_test.go
+++ b/models/renderhelper/repo_file_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -22,7 +21,7 @@ func TestRepoFile(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoFile(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -37,7 +36,7 @@ func TestRepoFile(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -49,13 +48,13 @@ func TestRepoFile(t *testing.T) {
assert.Equal(t,
`/test
./test
-
-
+
+
`, rendered)
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -63,12 +62,12 @@ func TestRepoFile(t *testing.T) {
`)
assert.NoError(t, err)
assert.Equal(t, `/test
-
+
`, rendered)
})
t.Run("WithCurrentRefPathByTag", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{
CurrentRefPath: "/commit/1234",
CurrentTreePath: "my-dir",
}).
@@ -78,7 +77,7 @@ func TestRepoFile(t *testing.T) {