diff --git a/.changelog.yml b/.changelog.yml
index a7df8779de..bfdee0c0ca 100644
--- a/.changelog.yml
+++ b/.changelog.yml
@@ -22,25 +22,20 @@ groups:
name: FEATURES
labels:
- type/feature
+ -
+ name: API
+ labels:
+ - modifies/api
-
name: ENHANCEMENTS
labels:
- type/enhancement
- -
- name: PERFORMANCE
- labels:
- - performance/memory
- - performance/speed
- - performance/bigrepo
- - performance/cpu
+ - type/refactoring
+ - topic/ui
-
name: BUGFIXES
labels:
- type/bug
- -
- name: API
- labels:
- - modifies/api
-
name: TESTING
labels:
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 104f1cfeed..9f9f6f27d1 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,13 +1,13 @@
{
"name": "Gitea DevContainer",
- "image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
+ "image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm",
"features": {
// installs nodejs into container
"ghcr.io/devcontainers/features/node:1": {
- "version": "lts"
+ "version": "20"
},
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
- "ghcr.io/devcontainers-extra/features/poetry:2": {},
+ "ghcr.io/devcontainers-contrib/features/poetry:2": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.12"
},
diff --git a/.dockerignore b/.dockerignore
index 843f12a7be..b696e1603c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -36,6 +36,15 @@ _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
@@ -70,6 +79,18 @@ 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 13aa8d50f0..c0946ac997 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,15 +12,11 @@ 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
deleted file mode 100644
index f9e1050240..0000000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,1004 +0,0 @@
-const vitestPlugin = require('@vitest/eslint-plugin');
-const restrictedSyntax = ['WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'];
-
-module.exports = {
- root: true,
- reportUnusedDisableDirectives: true,
- ignorePatterns: [
- '/web_src/js/vendor',
- '/web_src/fomantic',
- '/public/assets/js',
- ],
- parser: '@typescript-eslint/parser',
- parserOptions: {
- sourceType: 'module',
- ecmaVersion: 'latest',
- project: true,
- extraFileExtensions: ['.vue'],
- parser: '@typescript-eslint/parser', // for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
- },
- settings: {
- 'import-x/extensions': ['.js', '.ts'],
- 'import-x/parsers': {
- '@typescript-eslint/parser': ['.js', '.ts'],
- },
- 'import-x/resolver': {
- typescript: true,
- },
- },
- plugins: [
- '@eslint-community/eslint-plugin-eslint-comments',
- '@stylistic/eslint-plugin-js',
- '@typescript-eslint/eslint-plugin',
- 'eslint-plugin-array-func',
- 'eslint-plugin-github',
- 'eslint-plugin-import-x',
- 'eslint-plugin-no-jquery',
- 'eslint-plugin-no-use-extend-native',
- 'eslint-plugin-regexp',
- 'eslint-plugin-sonarjs',
- 'eslint-plugin-unicorn',
- 'eslint-plugin-wc',
- ],
- env: {
- es2024: true,
- node: true,
- },
- overrides: [
- {
- files: ['**/*.cjs'],
- rules: {
- 'import-x/no-commonjs': [0],
- '@typescript-eslint/no-require-imports': [0],
- },
- },
- {
- files: ['web_src/**/*'],
- globals: {
- __webpack_public_path__: true,
- process: false, // https://github.com/webpack/webpack/issues/15833
- },
- },
- {
- files: ['web_src/**/*', 'docs/**/*'],
- env: {
- browser: true,
- node: false,
- },
- },
- {
- files: ['*.config.*'],
- rules: {
- 'import-x/no-unused-modules': [0],
- },
- },
- {
- files: ['**/*.d.ts'],
- rules: {
- 'import-x/no-unused-modules': [0],
- '@typescript-eslint/consistent-type-definitions': [0],
- '@typescript-eslint/consistent-type-imports': [0],
- },
- },
- {
- files: ['web_src/js/types.ts'],
- rules: {
- 'import-x/no-unused-modules': [0],
- },
- },
- {
- files: ['**/*.test.*', 'web_src/js/test/setup.ts'],
- plugins: ['@vitest/eslint-plugin'],
- globals: vitestPlugin.environments.env.globals,
- rules: {
- '@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
new file mode 100644
index 0000000000..0dd9a6687d
--- /dev/null
+++ b/.eslintrc.yaml
@@ -0,0 +1,967 @@
+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 e218bbe25d..9fb4a4e83d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,7 +4,8 @@
/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 d409f18cd9..d37ce219c3 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://translate.gitea.com
+ url: https://crowdin.com/project/gitea
about: Translations are managed here.
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 0af43cd029..46efbcb194 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -41,7 +41,7 @@ modifies/internal:
- ".dockerignore"
- "docker/**"
- ".editorconfig"
- - ".eslintrc.cjs"
+ - ".eslintrc.yaml"
- ".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"
- - "web_src/**"
+ - "**/*.js"
+ - "**/*.ts"
+ - "**/*.vue"
docs-update-needed:
- changed-files:
diff --git a/.github/workflows/cron-licenses.yml b/.github/workflows/cron-licenses.yml
index c34066d318..cd8386ecc5 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-gitignore
+ - run: make generate-license 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 be27537924..7c1fb02442 100644
--- a/.github/workflows/files-changed.yml
+++ b/.github/workflows/files-changed.yml
@@ -51,16 +51,14 @@ jobs:
- "options/locale/locale_en-US.ini"
frontend:
- - "*.js"
- - "*.ts"
+ - "**/*.js"
- "web_src/**"
- - "tools/*.js"
- - "tools/*.ts"
- "assets/emoji.json"
- "package.json"
- "package-lock.json"
- "Makefile"
- - ".eslintrc.cjs"
+ - ".eslintrc.yaml"
+ - "stylelint.config.js"
- ".npmrc"
docs:
@@ -87,7 +85,6 @@ 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 f6720bf2f6..7e988e0449 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: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: pip install poetry
@@ -66,7 +66,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -95,7 +95,7 @@ jobs:
go-version-file: go.mod
check-latest: true
- run: make deps-backend deps-tools
- - run: make lint-go-windows lint-go-gitea-vet
+ - run: make lint-go-windows lint-go-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: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -186,7 +186,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 24
+ node-version: 22
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 55c2d2bf5e..0b23de0a66 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:14
+ image: postgres:12
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
@@ -98,7 +98,7 @@ jobs:
ports:
- "9200:9200"
meilisearch:
- image: getmeili/meilisearch:v1
+ image: getmeili/meilisearch:v1.2.0
env:
MEILI_ENV: development # disable auth
ports:
@@ -202,10 +202,12 @@ jobs:
test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
- runs-on: ubuntu-latest
+ # specifying the version of ubuntu in use as mssql fails on newer kernels
+ # pending resolution from vendor
+ runs-on: ubuntu-20.04
services:
mssql:
- image: mcr.microsoft.com/mssql/server:2019-latest
+ image: mcr.microsoft.com/mssql/server:2017-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 cc3fbd9c34..b84c69e4a0 100644
--- a/.github/workflows/pull-e2e-tests.yml
+++ b/.github/workflows/pull-e2e-tests.yml
@@ -12,9 +12,7 @@ jobs:
uses: ./.github/workflows/files-changed.yml
test-e2e:
- # 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
+ if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
@@ -25,7 +23,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 24
+ node-version: 22
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 c2cc14f771..2264c9e822 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: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -59,8 +59,6 @@ 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
@@ -87,27 +85,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: fetch go modules
run: make vendor
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64,linux/riscv64
+ platforms: linux/amd64,linux/arm64
push: true
- tags: |-
- gitea/gitea:${{ steps.clean_name.outputs.branch }}
- ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
+ tags: 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
@@ -134,12 +122,6 @@ 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
@@ -149,6 +131,4 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile.rootless
- tags: |-
- gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
- ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
+ tags: 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 c9c15c31a0..a406602dc0 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: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -69,8 +69,6 @@ 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,9 +79,7 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: |-
- gitea/gitea
- ghcr.io/go-gitea/gitea
+ images: gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
@@ -94,24 +90,16 @@ 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,linux/riscv64
+ platforms: linux/amd64,linux/arm64
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
@@ -122,9 +110,7 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: |-
- gitea/gitea
- ghcr.io/go-gitea/gitea
+ images: gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
@@ -137,17 +123,11 @@ 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,linux/riscv64
+ platforms: linux/amd64,linux/arm64
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 ae717c7cec..f67b76f408 100644
--- a/.github/workflows/release-tag-version.yml
+++ b/.github/workflows/release-tag-version.yml
@@ -14,8 +14,6 @@ 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
@@ -27,7 +25,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 24
+ node-version: 22
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -73,8 +71,6 @@ 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
@@ -85,34 +81,26 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: |-
- gitea/gitea
- ghcr.io/go-gitea/gitea
+ images: 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,linux/riscv64
+ platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -128,9 +116,7 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: |-
- gitea/gitea
- ghcr.io/go-gitea/gitea
+ images: gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless,onlatest=true
@@ -140,25 +126,19 @@ 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,linux/riscv64
+ platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }}
diff --git a/.gitignore b/.gitignore
index 0791a17c71..86e6e4fefd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,11 +9,6 @@ _test
# IntelliJ
.idea
-.run
-
-# IntelliJ Gateway
-.uuid
-
# Goland's output filename can not be set manually
/go_build_*
/gitea_*
@@ -22,9 +17,6 @@ _test
.vscode
__debug_bin*
-# Visual Studio
-/.vs/
-
*.cgo1.go
*.cgo2.c
_cgo_defun.c
@@ -42,10 +34,14 @@ _testmain.go
coverage.all
cpu.out
-/modules/migration/bindata.*
-/modules/options/bindata.*
-/modules/public/bindata.*
-/modules/templates/bindata.*
+/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
@@ -83,6 +79,18 @@ 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 70efd288ff..c39d7ac5f2 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,9 +1,7 @@
-version: "2"
-output:
- sort-order:
- - file
linters:
- default: none
+ enable-all: false
+ disable-all: true
+ fast: false
enable:
- bidichk
- depguard
@@ -11,170 +9,139 @@ linters:
- errcheck
- forbidigo
- gocritic
- - govet
- - ineffassign
- - mirror
- - nakedret
- - nolintlint
- - perfsprint
- - revive
- - staticcheck
- - testifylint
- - unconvert
- - unparam
- - unused
- - usestdlibvars
- - usetesting
- - wastedassign
- 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:
- - 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
-formatters:
- enable:
- gofmt
- gofumpt
- settings:
- gofumpt:
- extra-rules: true
- exclusions:
- generated: lax
- paths:
- - node_modules
- - public
- - web_src
- - third_party$
- - builtin$
- - examples$
+ - gosimple
+ - govet
+ - ineffassign
+ - nakedret
+ - nolintlint
+ - revive
+ - staticcheck
+ - stylecheck
+ - tenv
+ - testifylint
+ - typecheck
+ - unconvert
+ - unused
+ - unparam
+ - 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
+ 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
+
+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
diff --git a/.ignore b/.ignore
index 29912ad5c3..5b96dabd38 100644
--- a/.ignore
+++ b/.ignore
@@ -1,6 +1,9 @@
*.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
deleted file mode 100644
index 88ff1591a4..0000000000
--- a/.mailmap
+++ /dev/null
@@ -1,2 +0,0 @@
-Unknwon
-Unknwon 无闻
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b72ac4849a..3bb447f091 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,473 +4,6 @@ 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
@@ -952,36 +485,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* 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 11c99d1e3a..60146276db 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://translate.gitea.com).
+All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
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 c9e6a2d3db..b95ba83289 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
+FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.22
+FROM docker.io/library/alpine:3.21
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index 558e6cf73b..be6f125104 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
+FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.22
+FROM docker.io/library/alpine:3.21
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000
@@ -52,7 +52,6 @@ RUN apk --no-cache add \
git \
curl \
gnupg \
- openssh-keygen \
&& rm -rf /var/cache/apk/*
RUN addgroup \
diff --git a/MAINTAINERS b/MAINTAINERS
index 7643ab000f..ad02ecc755 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -31,6 +31,7 @@ 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)
@@ -62,6 +63,3 @@ 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 6a3fa60e49..5524cfb08c 100644
--- a/Makefile
+++ b/Makefile
@@ -23,21 +23,20 @@ SHASUM ?= shasum -a 256
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
COMMA := ,
-XGO_VERSION := go-1.24.x
+XGO_VERSION := go-1.23.x
AIR_PACKAGE ?= github.com/air-verse/air@v1
-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
+EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.0.3
+GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.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
+MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
+SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
-GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.1
-GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1
+GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.0
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@@ -74,13 +73,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)
@@ -110,17 +109,20 @@ 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/riscv64
+LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
+FOMANTIC_WORK_DIR := web_src/fomantic
+
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
-BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
+BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
+BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@@ -137,19 +139,25 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
-TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
+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)
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 *.cjs tests/e2e
+ESLINT_FILES := web_src/js tools *.js *.ts tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
-SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
+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))
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")
+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 += $(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
@@ -157,8 +165,10 @@ ifdef DEPS_PLAYWRIGHT
endif
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
-SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
+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_EXCLUDE := code.gitea.io/sdk
+SWAGGER_NEWLINE_COMMAND := -e '$$a\'
TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea
@@ -179,11 +189,67 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
all: build
.PHONY: help
-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"
+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"
.PHONY: go-check
go-check:
@@ -214,12 +280,12 @@ node-check:
fi
.PHONY: clean-all
-clean-all: clean ## delete backend, frontend and integration files
+clean-all: clean
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean
-clean: ## delete backend and integration files
- rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST_WILDCARD) \
+clean:
+ rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
integrations*.test \
e2e*.test \
tests/integration/gitea-integration-* \
@@ -230,7 +296,7 @@ clean: ## delete backend and integration files
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
.PHONY: fmt
-fmt: ## format the Go and template code
+fmt:
@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
@@ -245,20 +311,7 @@ fmt-check: fmt
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
- 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}"; \
+ echo "$${diff}"; \
exit 1; \
fi
@@ -272,95 +325,95 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
endif
.PHONY: generate-swagger
-generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
+generate-swagger: $(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)'
+$(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)'
.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:"; \
- printf "%s" "$${diff}"; \
+ echo "$${diff}"; \
exit 1; \
fi
.PHONY: swagger-validate
-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
+swagger-validate:
+ $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
- @$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
+ $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
.PHONY: checks
-checks: checks-frontend checks-backend ## run various consistency checks
+checks: checks-frontend checks-backend
.PHONY: checks-frontend
-checks-frontend: lockfile-check svg-check ## check frontend files
+checks-frontend: lockfile-check svg-check
.PHONY: checks-backend
-checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files
+checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check
.PHONY: lint
-lint: lint-frontend lint-backend lint-spell ## lint everything
+lint: lint-frontend lint-backend lint-spell
.PHONY: lint-fix
-lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
+lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix
.PHONY: lint-frontend
-lint-frontend: lint-js lint-css ## lint frontend files
+lint-frontend: lint-js lint-css
.PHONY: lint-frontend-fix
-lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
+lint-frontend-fix: lint-js-fix lint-css-fix
.PHONY: lint-backend
-lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
+lint-backend: lint-go lint-go-vet lint-go-gopls lint-editorconfig
.PHONY: lint-backend-fix
-lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
+lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
.PHONY: lint-js
-lint-js: node_modules ## lint js files
+lint-js: node_modules
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 files and fix issues
+lint-js-fix: node_modules
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 files
+lint-css: node_modules
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
.PHONY: lint-css-fix
-lint-css-fix: node_modules ## lint css files and fix issues
+lint-css-fix: node_modules
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
.PHONY: lint-swagger
-lint-swagger: node_modules ## lint swagger files
+lint-swagger: node_modules
npx spectral lint -q -F hint $(SWAGGER_SPEC)
.PHONY: lint-md
-lint-md: node_modules ## lint markdown files
+lint-md: node_modules
npx markdownlint *.md
.PHONY: lint-spell
-lint-spell: ## lint spelling
+lint-spell:
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
.PHONY: lint-spell-fix
-lint-spell-fix: ## lint spelling and fix issues
+lint-spell-fix:
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
.PHONY: lint-go
-lint-go: ## lint go files
+lint-go:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint-go-fix
-lint-go-fix: ## lint go files and fix issues
+lint-go-fix:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
# workaround step for the lint-go-windows CI task because 'go run' can not
@@ -370,58 +423,57 @@ lint-go-windows:
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
golangci-lint run
-.PHONY: lint-go-gitea-vet
-lint-go-gitea-vet: ## lint go files with gitea-vet
- @echo "Running gitea-vet..."
+.PHONY: lint-go-vet
+lint-go-vet:
+ @echo "Running go vet..."
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
@$(GO) vet -vettool=gitea-vet ./...
.PHONY: lint-go-gopls
-lint-go-gopls: ## lint go files with gopls
+lint-go-gopls:
@echo "Running gopls check..."
- @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
+ @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
.PHONY: lint-editorconfig
lint-editorconfig:
- @echo "Running editorconfig check..."
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
.PHONY: lint-actions
-lint-actions: ## lint action workflow files
+lint-actions:
$(GO) run $(ACTIONLINT_PACKAGE)
.PHONY: lint-templates
-lint-templates: .venv node_modules ## lint template files
+lint-templates: .venv node_modules
@node tools/lint-templates-svg.js
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
.PHONY: lint-yaml
-lint-yaml: .venv ## lint yaml files
- @poetry run yamllint -s .
+lint-yaml: .venv
+ @poetry run yamllint .
.PHONY: watch
-watch: ## watch everything and continuously rebuild
+watch:
@bash tools/watch.sh
.PHONY: watch-frontend
-watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
+watch-frontend: node-check node_modules
@rm -rf $(WEBPACK_DEST_ENTRIES)
NODE_ENV=development npx webpack --watch --progress
.PHONY: watch-backend
-watch-backend: go-check ## watch backend files and continuously rebuild
+watch-backend: go-check
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
.PHONY: test
-test: test-frontend test-backend ## test everything
+test: test-frontend test-backend
.PHONY: test-backend
-test-backend: ## test backend files
+test-backend:
@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 files
+test-frontend: node_modules
npx vitest
.PHONY: test-check
@@ -430,7 +482,7 @@ test-check:
@diff=$$(git status -s); \
if [ -n "$$diff" ]; then \
echo "make test-backend has changed files in the source tree:"; \
- printf "%s" "$${diff}"; \
+ echo "$${diff}"; \
echo "You should change the tests to create these files in a temporary directory."; \
echo "Do not simply add these files to .gitignore"; \
exit 1; \
@@ -453,7 +505,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: ## run go mod tidy
+tidy:
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
@@ -467,17 +519,15 @@ 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:"; \
- printf "%s" "$${diff}"; \
+ echo "$${diff}"; \
exit 1; \
fi
.PHONY: go-licenses
-go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
+go-licenses: $(GO_LICENSE_FILE)
$(GO_LICENSE_FILE): go.mod go.sum
- @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 $(GO_LICENSES_PACKAGE) 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)
@@ -721,17 +771,17 @@ install: $(wildcard *.go)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
-build: frontend backend ## build everything
+build: frontend backend
.PHONY: frontend
-frontend: $(WEBPACK_DEST) ## build frontend files
+frontend: $(WEBPACK_DEST)
.PHONY: backend
-backend: go-check generate-backend $(EXECUTABLE) ## build backend files
+backend: go-check generate-backend $(EXECUTABLE)
# 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 ## run "go generate"
+generate: generate-backend
.PHONY: generate-backend
generate-backend: $(TAGS_PREREQ) generate-go
@@ -743,7 +793,7 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check
security-check:
- go run $(GOVULNCHECK_PACKAGE) -show color ./...
+ go run $(GOVULNCHECK_PACKAGE) ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
@@ -796,20 +846,20 @@ release-sources: | $(DIST_DIRS)
rm -f $(STORED_VERSION_FILE)
.PHONY: deps
-deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies
+deps: deps-frontend deps-backend deps-tools deps-py
.PHONY: deps-py
-deps-py: .venv ## install python dependencies
+deps-py: .venv
.PHONY: deps-frontend
-deps-frontend: node_modules ## install frontend dependencies
+deps-frontend: node_modules
.PHONY: deps-backend
-deps-backend: ## install backend dependencies
+deps-backend:
$(GO) mod download
.PHONY: deps-tools
-deps-tools: ## install tool dependencies
+deps-tools:
$(GO) install $(AIR_PACKAGE) & \
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) install $(GOFUMPT_PACKAGE) & \
@@ -822,7 +872,6 @@ deps-tools: ## install tool dependencies
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
- $(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
wait
node_modules: package-lock.json
@@ -834,10 +883,10 @@ node_modules: package-lock.json
@touch .venv
.PHONY: update
-update: update-js update-py ## update js and py dependencies
+update: update-js update-py
.PHONY: update-js
-update-js: node-check | node_modules ## update js dependencies
+update-js: node-check | node_modules
npx updates -u -f package.json
rm -rf node_modules package-lock.json
npm install --package-lock
@@ -846,14 +895,27 @@ update-js: node-check | node_modules ## update js dependencies
@touch node_modules
.PHONY: update-py
-update-py: node-check | node_modules ## update py dependencies
+update-py: node-check | node_modules
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) ## build webpack files
+webpack: $(WEBPACK_DEST)
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules
@@ -863,7 +925,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@touch $(WEBPACK_DEST)
.PHONY: svg
-svg: node-check | node_modules ## build svg files
+svg: node-check | node_modules
rm -rf $(SVG_DEST_DIR)
node tools/generate-svg.js
@@ -873,7 +935,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:"; \
- printf "%s" "$${diff}"; \
+ echo "$${diff}"; \
exit 1; \
fi
@@ -884,7 +946,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:"; \
- printf "%s" "$${diff}"; \
+ echo "$${diff}"; \
exit 1; \
fi
@@ -898,8 +960,12 @@ 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: ## update gitignore files
+generate-gitignore:
$(GO) run build/generate-gitignores.go
.PHONY: generate-images
@@ -908,7 +974,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 017ca629d0..c280c832ac 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://translate.gitea.com "Crowdin")
+[](https://crowdin.com/project/gitea "Crowdin")
-[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
+[View this document in Chinese](./README_ZH.md)
## Purpose
@@ -31,14 +31,6 @@ 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:
@@ -60,8 +52,6 @@ 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]
@@ -78,25 +68,22 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
## Translating
-[](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.
+Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
-Get more information from [documentation](https://docs.gitea.com/contributing/localization).
+https://docs.gitea.com/contributing/localization
-## Official and Third-Party Projects
+[](https://crowdin.com/project/gitea)
-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.
+## Further information
-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.
+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/).
-## Communication
+We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
-[](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/).
+The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
## Authors
@@ -135,79 +122,18 @@ 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.
-## Further information
+## Screenshots
-
-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
deleted file mode 100644
index f34b25b945..0000000000
--- a/README.zh-cn.md
+++ /dev/null
@@ -1,206 +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://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
deleted file mode 100644
index 9de3f85dd5..0000000000
--- a/README.zh-tw.md
+++ /dev/null
@@ -1,206 +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://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
new file mode 100644
index 0000000000..a2e36dc22f
--- /dev/null
+++ b/README_ZH.md
@@ -0,0 +1,61 @@
+# 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 d961444239..6d1b2e1689 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -115,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/v2",
- "path": "github.com/RoaringBitmap/roaring/v2/LICENSE",
+ "name": "github.com/RoaringBitmap/roaring",
+ "path": "github.com/RoaringBitmap/roaring/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"
},
{
@@ -289,16 +289,6 @@
"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",
@@ -537,7 +527,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-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"
+ "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"
},
{
"name": "github.com/go-redsync/redsync/v4",
@@ -599,6 +589,11 @@
"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",
@@ -620,13 +615,8 @@
"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/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",
+ "name": "github.com/google/go-github/v61/github",
+ "path": "github.com/google/go-github/v61/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"
},
{
@@ -829,19 +819,14 @@
"path": "github.com/mattn/go-runewidth/LICENSE",
"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/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/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"
+ "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/mholt/acmez/v3",
- "path": "github.com/mholt/acmez/v3/LICENSE",
+ "name": "github.com/mholt/acmez/v2",
+ "path": "github.com/mholt/acmez/v2/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"
},
{
@@ -869,11 +854,6 @@
"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",
@@ -889,6 +869,11 @@
"path": "github.com/mitchellh/mapstructure/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Mitchell Hashimoto\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": "github.com/mmcloughlin/avo",
+ "path": "github.com/mmcloughlin/avo/LICENSE",
+ "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, Michael McLoughlin\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/modern-go/concurrent",
"path": "github.com/modern-go/concurrent/LICENSE",
@@ -1080,14 +1065,9 @@
"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-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/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/valyala/fastjson",
@@ -1097,7 +1077,7 @@
{
"name": "github.com/wneessen/go-mail",
"path": "github.com/wneessen/go-mail/LICENSE",
- "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"
+ "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"
},
{
"name": "github.com/wneessen/go-mail/smtp",
@@ -1109,11 +1089,21 @@
"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",
@@ -1139,11 +1129,6 @@
"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",
@@ -1164,11 +1149,6 @@
"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",
@@ -1214,6 +1194,11 @@
"path": "golang.org/x/time/rate/LICENSE",
"licenseText": "Copyright 2009 The Go Authors.\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 LLC 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": "golang.org/x/tools",
+ "path": "golang.org/x/tools/LICENSE",
+ "licenseText": "Copyright 2009 The Go Authors.\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 LLC 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": "google.golang.org/genproto/googleapis/rpc/status",
"path": "google.golang.org/genproto/googleapis/rpc/status/LICENSE",
diff --git a/build.go b/build.go
index e81ba54690..234579b514 100644
--- a/build.go
+++ b/build.go
@@ -5,10 +5,19 @@
package main
-// Libraries that are included to vendor utilities used during Makefile build.
+// Libraries that are included to vendor utilities used during 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 2553770762..2fcb7c2f2a 100644
--- a/build/generate-bindata.go
+++ b/build/generate-bindata.go
@@ -6,22 +6,87 @@
package main
import (
+ "bytes"
+ "crypto/sha1"
"fmt"
+ "log"
+ "net/http"
"os"
+ "path/filepath"
+ "strconv"
- "code.gitea.io/gitea/modules/assetfs"
+ "github.com/shurcooL/vfsgen"
)
-func main() {
- if len(os.Args) != 3 {
- fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}")
- os.Exit(1)
+func needsUpdate(dir, filename string) (bool, []byte) {
+ needRegen := false
+ _, err := os.Stat(filename)
+ if err != nil {
+ needRegen = true
}
- 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)
+ 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")
+ }
+
+ dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
+ var useGlobalModTime bool
+ if len(os.Args) == 5 {
+ useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
+ }
+
+ update, newHash := needsUpdate(dir, filename)
+
+ 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
new file mode 100644
index 0000000000..66e1d37755
--- /dev/null
+++ b/build/generate-licenses.go
@@ -0,0 +1,176 @@
+// 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
new file mode 100644
index 0000000000..7de1e6fbd6
--- /dev/null
+++ b/build/license/aliasgenerator.go
@@ -0,0 +1,41 @@
+// 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
new file mode 100644
index 0000000000..239181b736
--- /dev/null
+++ b/build/license/aliasgenerator_test.go
@@ -0,0 +1,39 @@
+// 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 2c51c6a1bc..f582c16c81 100644
--- a/cmd/actions.go
+++ b/cmd/actions.go
@@ -4,13 +4,12 @@
package cmd
import (
- "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -18,7 +17,7 @@ var (
CmdActions = &cli.Command{
Name: "actions",
Usage: "Manage Gitea Actions",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdActionsGenRunnerToken,
},
}
@@ -39,7 +38,10 @@ var (
}
)
-func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
+func runGenerateActionsRunnerToken(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setting.MustInstalled()
scope := c.String("scope")
diff --git a/cmd/admin.go b/cmd/admin.go
index 559544edd3..6c9480e76e 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/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -23,7 +23,7 @@ var (
CmdAdmin = &cli.Command{
Name: "admin",
Usage: "Perform common administrative operations",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
subcmdRegenerate,
@@ -41,7 +41,7 @@ var (
subcmdRegenerate = &cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
@@ -50,15 +50,15 @@ var (
subcmdAuth = &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
- Commands: []*cli.Command{
- microcmdAuthAddOauth(),
- microcmdAuthUpdateOauth(),
- microcmdAuthAddLdapBindDn(),
- microcmdAuthUpdateLdapBindDn(),
- microcmdAuthAddLdapSimpleAuth(),
- microcmdAuthUpdateLdapSimpleAuth(),
- microcmdAuthAddSMTP(),
- microcmdAuthUpdateSMTP(),
+ Subcommands: []*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",
- Required: true,
+ Name: "title",
+ Usage: `a title of a message`,
+ Value: "",
},
&cli.StringFlag{
Name: "content",
@@ -86,16 +86,17 @@ var (
},
},
}
-)
-func idFlag() *cli.Int64Flag {
- return &cli.Int64Flag{
+ idFlag = &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
}
@@ -106,7 +107,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) 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 1a09366722..4777a92908 100644
--- a/cmd/admin_auth.go
+++ b/cmd/admin_auth.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"fmt"
"os"
@@ -14,14 +13,14 @@ import (
"code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var (
microcmdAuthDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
- Flags: []cli.Flag{idFlag()},
+ Flags: []cli.Flag{idFlag},
Action: runDeleteAuth,
}
microcmdAuthList = &cli.Command{
@@ -57,7 +56,10 @@ var (
}
)
-func runListAuth(ctx context.Context, c *cli.Command) error {
+func runListAuth(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if err := initDB(ctx); err != nil {
return err
}
@@ -88,11 +90,14 @@ func runListAuth(ctx context.Context, c *cli.Command) error {
return nil
}
-func runDeleteAuth(ctx context.Context, c *cli.Command) error {
+func runDeleteAuth(c *cli.Context) 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 069ad6600c..aff2a12855 100644
--- a/cmd/admin_auth_ldap.go
+++ b/cmd/admin_auth_ldap.go
@@ -9,10 +9,9 @@ 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/v3"
+ "github.com/urfave/cli/v2"
)
type (
@@ -24,8 +23,8 @@ type (
}
)
-func commonLdapCLIFlags() []cli.Flag {
- return []cli.Flag{
+var (
+ commonLdapCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
@@ -103,10 +102,8 @@ func commonLdapCLIFlags() []cli.Flag {
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
},
}
-}
-func ldapBindDnCLIFlags() []cli.Flag {
- return append(commonLdapCLIFlags(),
+ ldapBindDnCLIFlags = append(commonLdapCLIFlags,
&cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
@@ -130,88 +127,50 @@ func ldapBindDnCLIFlags() []cli.Flag {
&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",
})
-}
-func ldapSimpleAuthCLIFlags() []cli.Flag {
- return append(commonLdapCLIFlags(),
+ ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
&cli.StringFlag{
Name: "user-dn",
Usage: "The user's DN.",
})
-}
-func microcmdAuthAddLdapBindDn() *cli.Command {
- return &cli.Command{
+ microcmdAuthAddLdapBindDn = &cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
- Action: func(ctx context.Context, cmd *cli.Command) error {
- return newAuthService().addLdapBindDn(ctx, cmd)
+ Action: func(c *cli.Context) error {
+ return newAuthService().addLdapBindDn(c)
},
- Flags: ldapBindDnCLIFlags(),
+ Flags: ldapBindDnCLIFlags,
}
-}
-func microcmdAuthUpdateLdapBindDn() *cli.Command {
- return &cli.Command{
+ microcmdAuthUpdateLdapBindDn = &cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
- Action: func(ctx context.Context, cmd *cli.Command) error {
- return newAuthService().updateLdapBindDn(ctx, cmd)
+ Action: func(c *cli.Context) error {
+ return newAuthService().updateLdapBindDn(c)
},
- Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
+ Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
}
-}
-func microcmdAuthAddLdapSimpleAuth() *cli.Command {
- return &cli.Command{
+ microcmdAuthAddLdapSimpleAuth = &cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
- Action: func(ctx context.Context, cmd *cli.Command) error {
- return newAuthService().addLdapSimpleAuth(ctx, cmd)
+ Action: func(c *cli.Context) error {
+ return newAuthService().addLdapSimpleAuth(c)
},
- Flags: ldapSimpleAuthCLIFlags(),
+ Flags: ldapSimpleAuthCLIFlags,
}
-}
-func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
- return &cli.Command{
+ microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
- Action: func(ctx context.Context, cmd *cli.Command) error {
- return newAuthService().updateLdapSimpleAuth(ctx, cmd)
+ Action: func(c *cli.Context) error {
+ return newAuthService().updateLdapSimpleAuth(c)
},
- Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
+ Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
}
-}
+)
// newAuthService creates a service with default functions.
func newAuthService() *authService {
@@ -223,8 +182,8 @@ func newAuthService() *authService {
}
}
-// parseAuthSourceLdap assigns values on authSource according to command line flags.
-func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
+// parseAuthSource assigns values on authSource according to command line flags.
+func parseAuthSource(c *cli.Context, authSource *auth.Source) {
if c.IsSet("name") {
authSource.Name = c.String("name")
}
@@ -240,11 +199,10 @@ func parseAuthSourceLdap(c *cli.Command, 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.Command, config *ldap.Source) error {
+func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("name") {
config.Name = c.String("name")
}
@@ -257,7 +215,7 @@ func parseLdapConfig(c *cli.Command, 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
}
@@ -312,26 +270,8 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("allow-deactivate-all") {
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
}
- 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")
+ if c.IsSet("skip-local-2fa") {
+ config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
}
return nil
}
@@ -349,27 +289,32 @@ 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.Command, authType auth.Type) (*auth.Source, error) {
+func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, 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(ctx context.Context, c *cli.Command) error {
+func (a *authService) addLdapBindDn(c *cli.Context) 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
}
@@ -382,7 +327,7 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
},
}
- parseAuthSourceLdap(c, authSource)
+ parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -391,7 +336,10 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
}
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
-func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
+func (a *authService) updateLdapBindDn(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if err := a.initDB(ctx); err != nil {
return err
}
@@ -401,7 +349,7 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
return err
}
- parseAuthSourceLdap(c, authSource)
+ parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -410,11 +358,14 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
}
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
-func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
+func (a *authService) addLdapSimpleAuth(c *cli.Context) 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
}
@@ -427,7 +378,7 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
},
}
- parseAuthSourceLdap(c, authSource)
+ parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -436,7 +387,10 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
}
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
-func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
+func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if err := a.initDB(ctx); err != nil {
return err
}
@@ -446,7 +400,7 @@ func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command)
return err
}
- parseAuthSourceLdap(c, authSource)
+ parseAuthSource(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 2da7ebc573..7791f3a9cc 100644
--- a/cmd/admin_auth_ldap_test.go
+++ b/cmd/admin_auth_ldap_test.go
@@ -8,16 +8,17 @@ 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/v3"
+ "github.com/urfave/cli/v2"
)
func TestAddLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
+ osExiter := cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
// Test cases
cases := []struct {
@@ -50,13 +51,6 @@ 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,
@@ -84,13 +78,6 @@ 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,
},
},
},
@@ -134,7 +121,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
{
@@ -228,23 +215,22 @@ func TestAddLdapBindDn(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := cli.Command{
- Flags: microcmdAuthAddLdapBindDn().Flags,
- Action: service.addLdapBindDn,
- }
+ app := cli.NewApp()
+ app.Flags = microcmdAuthAddLdapBindDn.Flags
+ app.Action = service.addLdapBindDn
// Run it
- err := app.Run(t.Context(), c.args)
+ err := app.Run(c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -256,7 +242,9 @@ func TestAddLdapBindDn(t *testing.T) {
func TestAddLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
+ osExiter := cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
// Test cases
cases := []struct {
@@ -346,12 +334,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
"--name", "ldap (simple auth) source",
"--security-protocol", "zzzzz",
"--host", "ldap-server",
- "--port", "1234",
+ "--port", "123",
"--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
{
@@ -458,23 +446,22 @@ func TestAddLdapSimpleAuth(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := &cli.Command{
- Flags: microcmdAuthAddLdapSimpleAuth().Flags,
- Action: service.addLdapSimpleAuth,
- }
+ app := cli.NewApp()
+ app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
+ app.Action = service.addLdapSimpleAuth
// Run it
- err := app.Run(t.Context(), c.args)
+ err := app.Run(c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -486,7 +473,9 @@ func TestAddLdapSimpleAuth(t *testing.T) {
func TestUpdateLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
+ osExiter := cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
// Test cases
cases := []struct {
@@ -521,13 +510,6 @@ 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{
@@ -563,13 +545,6 @@ 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,
},
},
},
@@ -861,7 +836,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
{
@@ -880,7 +855,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
{
@@ -922,7 +897,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -944,12 +919,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
}
// Create a copy of command to test
- app := cli.Command{
- Flags: microcmdAuthUpdateLdapBindDn().Flags,
- Action: service.updateLdapBindDn,
- }
+ app := cli.NewApp()
+ app.Flags = microcmdAuthUpdateLdapBindDn.Flags
+ app.Action = service.updateLdapBindDn
+
// Run it
- err := app.Run(t.Context(), c.args)
+ err := app.Run(c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -961,7 +936,9 @@ func TestUpdateLdapBindDn(t *testing.T) {
func TestUpdateLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
+ osExiter := cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
// Test cases
cases := []struct {
@@ -1252,7 +1229,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
{
@@ -1271,7 +1248,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
{
@@ -1310,7 +1287,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -1332,12 +1309,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
}
// Create a copy of command to test
- app := cli.Command{
- Flags: microcmdAuthUpdateLdapSimpleAuth().Flags,
- Action: service.updateLdapSimpleAuth,
- }
+ app := cli.NewApp()
+ app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
+ app.Action = service.updateLdapSimpleAuth
+
// Run it
- err := app.Run(t.Context(), c.args)
+ err := app.Run(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 d1aa753500..8e6239ac33 100644
--- a/cmd/admin_auth_oauth.go
+++ b/cmd/admin_auth_oauth.go
@@ -4,20 +4,18 @@
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/v3"
+ "github.com/urfave/cli/v2"
)
-func oauthCLIFlags() []cli.Flag {
- return []cli.Flag{
+var (
+ oauthCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -122,34 +120,23 @@ func oauthCLIFlags() []cli.Flag {
Usage: "Activate automatic team membership removal depending on groups",
},
}
-}
-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(),
+ microcmdAuthAddOauth = &cli.Command{
+ Name: "add-oauth",
+ Usage: "Add new Oauth authentication source",
+ Action: runAddOauth,
+ Flags: oauthCLIFlags,
}
-}
-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:]...)...),
+ 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 parseOAuth2Config(c *cli.Command) *oauth2.Source {
+func parseOAuth2Config(c *cli.Context) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
@@ -169,6 +156,7 @@ func parseOAuth2Config(c *cli.Command) *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"),
@@ -180,8 +168,11 @@ func parseOAuth2Config(c *cli.Command) *oauth2.Source {
}
}
-func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
- if err := a.initDB(ctx); err != nil {
+func runAddOauth(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(ctx); err != nil {
return err
}
@@ -193,25 +184,27 @@ func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
}
}
- 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", ""),
+ return auth_model.CreateSource(ctx, &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: c.String("name"),
+ IsActive: true,
+ Cfg: config,
})
}
-func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error {
+func runUpdateOauth(c *cli.Context) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- if err := a.initDB(ctx); err != nil {
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(ctx); err != nil {
return err
}
- source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
+ source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -301,6 +294,6 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
- source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
- return a.updateAuthSource(ctx, source)
+
+ return auth_model.UpdateSource(ctx, source)
}
diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go
deleted file mode 100644
index df1bd9c1a6..0000000000
--- a/cmd/admin_auth_oauth_test.go
+++ /dev/null
@@ -1,333 +0,0 @@
-// 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_smtp_test.go b/cmd/admin_auth_smtp_test.go
deleted file mode 100644
index e54e01830c..0000000000
--- a/cmd/admin_auth_smtp_test.go
+++ /dev/null
@@ -1,271 +0,0 @@
-// 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_auth_smtp.go b/cmd/admin_auth_stmp.go
similarity index 67%
rename from cmd/admin_auth_smtp.go
rename to cmd/admin_auth_stmp.go
index 93e0587fc3..d724746905 100644
--- a/cmd/admin_auth_smtp.go
+++ b/cmd/admin_auth_stmp.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"strings"
@@ -12,11 +11,11 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/smtp"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-func smtpCLIFlags() []cli.Flag {
- return []cli.Flag{
+var (
+ smtpCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -39,10 +38,12 @@ func smtpCLIFlags() []cli.Flag {
&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",
@@ -52,6 +53,7 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
+ Value: true,
},
&cli.StringFlag{
Name: "allowed-domains",
@@ -61,6 +63,7 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
+ Value: true,
},
&cli.BoolFlag{
Name: "active",
@@ -68,34 +71,23 @@ func smtpCLIFlags() []cli.Flag {
Value: true,
},
}
-}
-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:]...)...),
+ microcmdAuthAddSMTP = &cli.Command{
+ Name: "add-smtp",
+ Usage: "Add new SMTP authentication source",
+ Action: runAddSMTP,
+ Flags: smtpCLIFlags,
}
-}
-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(),
+ 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 parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
+func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@@ -125,11 +117,17 @@ func parseSMTPConfig(c *cli.Command, 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 (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
- if err := a.initDB(ctx); err != nil {
+func runAddSMTP(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(ctx); err != nil {
return err
}
@@ -157,25 +155,27 @@ func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
smtpConfig.Auth = "PLAIN"
}
- 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", ""),
+ return auth_model.CreateSource(ctx, &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: c.String("name"),
+ IsActive: active,
+ Cfg: &smtpConfig,
})
}
-func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
+func runUpdateSMTP(c *cli.Context) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- if err := a.initDB(ctx); err != nil {
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(ctx); err != nil {
return err
}
- source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
+ source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -195,6 +195,6 @@ func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
}
source.Cfg = smtpConfig
- source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
- return a.updateAuthSource(ctx, source)
+
+ return auth_model.UpdateSource(ctx, source)
}
diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go
index a5f1bd5105..ab769f6d0c 100644
--- a/cmd/admin_regenerate.go
+++ b/cmd/admin_regenerate.go
@@ -4,13 +4,11 @@
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/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -27,14 +25,20 @@ var (
}
)
-func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
+func runRegenerateHooks(_ *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if err := initDB(ctx); err != nil {
return err
}
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
}
-func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
+func runRegenerateKeys(_ *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_user.go b/cmd/admin_user.go
index 3a24c3e56f..967a6ed88a 100644
--- a/cmd/admin_user.go
+++ b/cmd/admin_user.go
@@ -4,18 +4,18 @@
package cmd
import (
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var subcmdUser = &cli.Command{
Name: "user",
Usage: "Modify users",
- Commands: []*cli.Command{
- microcmdUserCreate(),
+ Subcommands: []*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 c27905b4db..f1ed46e70b 100644
--- a/cmd/admin_user_change_password.go
+++ b/cmd/admin_user_change_password.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"fmt"
@@ -14,41 +13,44 @@ import (
"code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-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,
- },
+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",
},
- }
+ &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(ctx context.Context, c *cli.Command) error {
- if !setting.IsInTesting {
- if err := initDB(ctx); err != nil {
- return err
- }
+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
}
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
deleted file mode 100644
index 17d0382af7..0000000000
--- a/cmd/admin_user_change_password_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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 cbdb5f90e2..bf8cbc7c4c 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -7,7 +7,6 @@ import (
"context"
"errors"
"fmt"
- "strings"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
@@ -16,110 +15,73 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-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,
- },
+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",
},
- 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: "username",
+ Usage: "Username",
},
- }
+ &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(ctx context.Context, c *cli.Command) error {
+func runCreateUser(c *cli.Context) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings()
- userTypes := map[string]user_model.UserType{
- "individual": user_model.UserTypeIndividual,
- "bot": user_model.UserTypeBot,
+ if err := argsSet(c, "email"); err != nil {
+ return err
}
- userType, ok := userTypes[c.String("user-type")]
- if !ok {
- return fmt.Errorf("invalid user type: %s", c.String("user-type"))
+
+ if c.IsSet("name") && c.IsSet("username") {
+ return errors.New("cannot set both --name and --username flags")
}
- if userType != user_model.UserTypeIndividual {
- // Some other commands like "change-password" also only support individual users.
- // It needs to clarify the "password" behavior for bot users in the future.
- // At the moment, we do not allow setting password for bot users.
- if c.IsSet("password") || c.IsSet("random-password") {
- return errors.New("password can only be set for individual users")
- }
+ if !c.IsSet("name") && !c.IsSet("username") {
+ return errors.New("one of --name or --username flags must be set")
}
if c.IsSet("password") && c.IsSet("random-password") {
@@ -131,12 +93,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
username = c.String("username")
} else {
username = c.String("name")
- _, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
+ _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
}
+ ctx := c.Context
if !setting.IsInTesting {
- // FIXME: need to refactor the "initDB" related code later
+ // FIXME: need to refactor the "installSignals/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
}
@@ -152,19 +118,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
return err
}
fmt.Printf("generated random password is '%s'\n", password)
- } else if userType == user_model.UserTypeIndividual {
+ } else {
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 if userType == user_model.UserTypeIndividual {
+ } else {
// check whether there are users in the database
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
if err != nil {
@@ -188,12 +151,10 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
u := &user_model.User{
Name: username,
Email: c.String("email"),
- IsAdmin: isAdmin,
- Type: userType,
Passwd: password,
+ IsAdmin: isAdmin,
MustChangePassword: mustChangePassword,
Visibility: visibility,
- FullName: c.String("fullname"),
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -201,40 +162,23 @@ func runCreateUser(ctx context.Context, c *cli.Command) 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)
- // create the access token
- if accessTokenScope != "" {
- t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
+ if c.Bool("access-token") {
+ t := &auth_model.AccessToken{
+ Name: "gitea-admin",
+ UID: u.ID,
+ }
+
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 437e07d9a2..83754e97b1 100644
--- a/cmd/admin_user_create_test.go
+++ b/cmd/admin_user_create_test.go
@@ -8,127 +8,37 @@ 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() {
- 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{}))
+ assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
}
- 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...))
+ type createCheck struct{ IsAdmin, MustChangePassword bool }
+ createUser := func(name, args string) createCheck {
+ assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
+ return createCheck{u.IsAdmin, u.MustChangePassword}
}
+ reset()
+ assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
- t.Run("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: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
- 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)
- })
+ 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"))
}
diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go
index f91041577c..520557554a 100644
--- a/cmd/admin_user_delete.go
+++ b/cmd/admin_user_delete.go
@@ -4,56 +4,53 @@
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/v3"
+ "github.com/urfave/cli/v2"
)
-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",
- },
+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",
},
- Action: runDeleteUser,
- }
+ &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,
}
-func runDeleteUser(ctx context.Context, c *cli.Command) error {
+func runDeleteUser(c *cli.Context) 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")
}
- if !setting.IsInTesting {
- if err := initDB(ctx); err != nil {
- return err
- }
+ ctx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(ctx); err != nil {
+ return err
}
if err := storage.Init(); err != nil {
@@ -73,11 +70,11 @@ func runDeleteUser(ctx context.Context, c *cli.Command) 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
deleted file mode 100644
index d0330582d7..0000000000
--- a/cmd/admin_user_delete_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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 61064fdef4..6c2c10494e 100644
--- a/cmd/admin_user_generate_access_token.go
+++ b/cmd/admin_user_generate_access_token.go
@@ -4,14 +4,13 @@
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/v3"
+ "github.com/urfave/cli/v2"
)
var microcmdUserGenerateAccessToken = &cli.Command{
@@ -35,18 +34,21 @@ var microcmdUserGenerateAccessToken = &cli.Command{
},
&cli.StringFlag{
Name: "scopes",
- Value: "all",
- Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
+ Value: "",
+ Usage: "Comma separated list of scopes to apply to access token",
},
},
Action: runGenerateAccessToken,
}
-func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
+func runGenerateAccessToken(c *cli.Context) 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
}
@@ -75,9 +77,6 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) 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 e3d345e2f2..4c2b26d1df 100644
--- a/cmd/admin_user_list.go
+++ b/cmd/admin_user_list.go
@@ -4,14 +4,13 @@
package cmd
import (
- "context"
"fmt"
"os"
"text/tabwriter"
user_model "code.gitea.io/gitea/models/user"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var microcmdUserList = &cli.Command{
@@ -26,7 +25,10 @@ var microcmdUserList = &cli.Command{
},
}
-func runListUsers(ctx context.Context, c *cli.Command) error {
+func runListUsers(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
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 8521853dc1..2794414259 100644
--- a/cmd/admin_user_must_change_password.go
+++ b/cmd/admin_user_must_change_password.go
@@ -4,41 +4,40 @@
package cmd
import (
- "context"
"errors"
"fmt"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-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",
- },
+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",
},
- }
+ &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(ctx context.Context, c *cli.Command) error {
+func runMustChangePassword(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
if c.NArg() == 0 && !c.IsSet("all") {
return errors.New("either usernames or --all must be provided")
}
@@ -47,10 +46,8 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
all := c.Bool("all")
exclude := c.StringSlice("exclude")
- if !setting.IsInTesting {
- if err := initDB(ctx); err != nil {
- return err
- }
+ 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
deleted file mode 100644
index a6611fdc04..0000000000
--- a/cmd/admin_user_must_change_password_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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 53b4f9dcb4..38241d71a3 100644
--- a/cmd/cert.go
+++ b/cmd/cert.go
@@ -6,7 +6,6 @@
package cmd
import (
- "context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -14,7 +13,6 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
- "fmt"
"log"
"math/big"
"net"
@@ -22,59 +20,47 @@ import (
"strings"
"time"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-// 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.
+// 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.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
- 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",
- },
+ Action: runCert,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "host",
+ Value: "",
+ Usage: "Comma-separated hostnames and IPs to generate a certificate for",
},
- }
+ &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 {
@@ -103,7 +89,11 @@ func pemBlockForKey(priv any) *pem.Block {
}
}
-func runCert(_ context.Context, c *cli.Command) error {
+func runCert(c *cli.Context) error {
+ if err := argsSet(c, "host"); err != nil {
+ return err
+ }
+
var priv any
var err error
switch c.String("ecdsa-curve") {
@@ -118,17 +108,17 @@ func runCert(_ context.Context, c *cli.Command) error {
case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
- err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
+ log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
}
if err != nil {
- return fmt.Errorf("failed to generate private key: %w", err)
+ log.Fatalf("Failed to generate private key: %v", 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 {
- return fmt.Errorf("failed to parse creation date %w", err)
+ log.Fatalf("Failed to parse creation date: %v", err)
}
} else {
notBefore = time.Now()
@@ -139,7 +129,7 @@ func runCert(_ context.Context, c *cli.Command) error {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
- return fmt.Errorf("failed to generate serial number: %w", err)
+ log.Fatalf("Failed to generate serial number: %v", err)
}
template := x509.Certificate{
@@ -156,8 +146,8 @@ func runCert(_ context.Context, c *cli.Command) error {
BasicConstraintsValid: true,
}
- hosts := strings.SplitSeq(c.String("host"), ",")
- for h := range hosts {
+ hosts := strings.Split(c.String("host"), ",")
+ for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
@@ -172,35 +162,35 @@ func runCert(_ context.Context, c *cli.Command) error {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil {
- return fmt.Errorf("failed to create certificate: %w", err)
+ log.Fatalf("Failed to create certificate: %v", err)
}
- certOut, err := os.Create(c.String("out"))
+ certOut, err := os.Create("cert.pem")
if err != nil {
- return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
+ log.Fatalf("Failed to open cert.pem for writing: %v", err)
}
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
- return fmt.Errorf("failed to encode certificate: %w", err)
+ log.Fatalf("Failed to encode certificate: %v", err)
}
err = certOut.Close()
if err != nil {
- return fmt.Errorf("failed to write cert: %w", err)
+ log.Fatalf("Failed to write cert: %v", err)
}
- fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out"))
+ log.Println("Written cert.pem")
- keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
+ keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
- return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
+ log.Fatalf("Failed to open key.pem for writing: %v", err)
}
err = pem.Encode(keyOut, pemBlockForKey(priv))
if err != nil {
- return fmt.Errorf("failed to encode key: %w", err)
+ log.Fatalf("Failed to encode key: %v", err)
}
err = keyOut.Close()
if err != nil {
- return fmt.Errorf("failed to write key: %w", err)
+ log.Fatalf("Failed to write key: %v", err)
}
- fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout"))
+ log.Println("Written key.pem")
return nil
}
diff --git a/cmd/cert_test.go b/cmd/cert_test.go
deleted file mode 100644
index 4242d8915b..0000000000
--- a/cmd/cert_test.go
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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 5b96bcbf9a..423dce2674 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -18,19 +18,20 @@ 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/v3"
+ "github.com/urfave/cli/v2"
)
// 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.Command, args ...string) error {
+func argsSet(c *cli.Context, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
return errors.New(a + " is not set")
}
- if c.Value(a) == nil {
+ if util.IsEmptyString(c.String(a)) {
return errors.New(a + " is required")
}
}
@@ -108,7 +109,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
}
-func globalBool(c *cli.Command, name string) bool {
+func globalBool(c *cli.Context, name string) bool {
for _, ctx := range c.Lineage() {
if ctx.Bool(name) {
return true
@@ -119,8 +120,8 @@ func globalBool(c *cli.Command, 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(context.Context, *cli.Command) (context.Context, error) {
- return func(ctx context.Context, c *cli.Command) (context.Context, error) {
+func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
+ return func(c *cli.Context) error {
level := defaultLevel
if globalBool(c, "quiet") {
level = log.FATAL
@@ -129,16 +130,6 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
level = log.TRACE
}
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
- return ctx, nil
+ return 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
deleted file mode 100644
index a36d05c76e..0000000000
--- a/cmd/cmd_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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 098c0e9a8a..605d02e3ef 100644
--- a/cmd/docs.go
+++ b/cmd/docs.go
@@ -4,13 +4,11 @@
package cmd
import (
- "context"
"fmt"
"os"
"strings"
- cli_docs "github.com/urfave/cli-docs/v3"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// CmdDocs represents the available docs sub-command.
@@ -32,16 +30,16 @@ var CmdDocs = &cli.Command{
},
}
-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())
+func runDocs(ctx *cli.Context) error {
+ docs, err := ctx.App.ToMarkdown()
+ if ctx.Bool("man") {
+ docs, err = ctx.App.ToMan()
}
if err != nil {
return err
}
- if !cmd.Bool("man") {
+ if !ctx.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
@@ -53,8 +51,8 @@ func runDocs(_ context.Context, cmd *cli.Command) error {
}
out := os.Stdout
- if cmd.String("output") != "" {
- fi, err := os.Create(cmd.String("output"))
+ if ctx.String("output") != "" {
+ fi, err := os.Create(ctx.String("output"))
if err != nil {
return err
}
diff --git a/cmd/doctor.go b/cmd/doctor.go
index 9e0fcbf877..e433f4adc5 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"fmt"
golog "log"
"os"
@@ -20,7 +19,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/doctor"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
"xorm.io/xorm"
)
@@ -30,7 +29,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.",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
@@ -93,13 +92,16 @@ You should back-up your database before doing this and ensure that your database
Action: runRecreateTable,
}
-func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
+func runRecreateTable(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
// Redirect the default golog to here
golog.SetFlags(0)
golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
- debug := cmd.Bool("debug")
+ debug := ctx.Bool("debug")
setting.MustInstalled()
setting.LoadDBSetting()
@@ -110,15 +112,15 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
}
setting.Database.LogSQL = debug
- if err := db.InitEngine(ctx); err != nil {
+ if err := db.InitEngine(stdCtx); 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 := cmd.Args()
- names := make([]string, 0, cmd.NArg())
- for i := 0; i < cmd.NArg(); i++ {
+ args := ctx.Args()
+ names := make([]string, 0, ctx.NArg())
+ for i := 0; i < ctx.NArg(); i++ {
names = append(names, args.Get(i))
}
@@ -128,25 +130,24 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
}
recreateTables := migrate_base.RecreateTables(beans...)
- return db.InitEngineWithMigration(ctx, func(ctx context.Context, x *xorm.Engine) error {
- if err := migrations.EnsureUpToDate(ctx, x); err != nil {
+ return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
+ if err := migrations.EnsureUpToDate(x); err != nil {
return err
}
return recreateTables(x)
})
}
-func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
+func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
// Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
- logFile := cmd.String("log-file")
- switch logFile {
- case "":
+ logFile := ctx.String("log-file")
+ if logFile == "" {
return // if no doctor log-file is set, do not show any log from default logger
- case "-":
+ } else if logFile == "-" {
setupConsoleLogger(log.TRACE, colorize, os.Stdout)
- default:
+ } else {
logFile, _ = filepath.Abs(logFile)
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
@@ -158,20 +159,23 @@ func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
}
}
-func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
+func runDoctorCheck(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
colorize := log.CanColorStdout
- if cmd.IsSet("color") {
- colorize = cmd.Bool("color")
+ if ctx.IsSet("color") {
+ colorize = ctx.Bool("color")
}
- setupDoctorDefaultLogger(cmd, colorize)
+ setupDoctorDefaultLogger(ctx, 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 cmd.IsSet("list") {
+ if ctx.IsSet("list") {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
doctor.SortChecks(doctor.Checks)
@@ -189,12 +193,12 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
}
var checks []*doctor.Check
- if cmd.Bool("all") {
+ if ctx.Bool("all") {
checks = make([]*doctor.Check, len(doctor.Checks))
copy(checks, doctor.Checks)
- } else if cmd.IsSet("run") {
- addDefault := cmd.Bool("default")
- runNamesSet := container.SetOf(cmd.StringSlice("run")...)
+ } else if ctx.IsSet("run") {
+ addDefault := ctx.Bool("default")
+ runNamesSet := container.SetOf(ctx.StringSlice("run")...)
for _, check := range doctor.Checks {
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
checks = append(checks, check)
@@ -211,5 +215,5 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
}
}
}
- return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks)
+ return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
}
diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go
index 8cb718d383..48c835ad0e 100644
--- a/cmd/doctor_convert.go
+++ b/cmd/doctor_convert.go
@@ -4,14 +4,13 @@
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/v3"
+ "github.com/urfave/cli/v2"
)
// cmdDoctorConvert represents the available convert sub-command.
@@ -22,8 +21,11 @@ var cmdDoctorConvert = &cli.Command{
Action: runDoctorConvert,
}
-func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
- if err := initDB(ctx); err != nil {
+func runDoctorConvert(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(stdCtx); err != nil {
return err
}
diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go
index da942b38b6..3e1ff299c5 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/v3"
+ "github.com/urfave/cli/v2"
)
func TestDoctorRun(t *testing.T) {
@@ -22,13 +22,12 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true,
})
- app := &cli.Command{
- Commands: []*cli.Command{cmdDoctorCheck},
- }
- err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
+ app := cli.NewApp()
+ app.Commands = []*cli.Command{cmdDoctorCheck}
+ err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
assert.NoError(t, err)
- err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
+ err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
- err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
+ err = app.Run([]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 ed19e3d4bf..ececc80f72 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -5,7 +5,7 @@
package cmd
import (
- "context"
+ "fmt"
"os"
"path"
"path/filepath"
@@ -21,7 +21,7 @@ import (
"gitea.com/go-chi/session"
"github.com/mholt/archiver/v3"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// CmdDump represents the available dump sub-command.
@@ -93,7 +93,7 @@ var CmdDump = &cli.Command{
},
&cli.StringFlag{
Name: "type",
- Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
+ Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
},
},
}
@@ -102,17 +102,17 @@ func fatal(format string, args ...any) {
log.Fatal(format, args...)
}
-func runDump(ctx context.Context, cmd *cli.Command) error {
+func runDump(ctx *cli.Context) error {
setting.MustInstalled()
- quite := cmd.Bool("quiet")
- verbose := cmd.Bool("verbose")
+ quite := ctx.Bool("quiet")
+ verbose := ctx.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(cmd.String("file"), cmd.String("type"))
+ outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
if outType == "" {
fatal("Invalid output type")
}
@@ -137,7 +137,10 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
setting.DisableLoggerInit()
setting.LoadSettings() // cannot access session settings otherwise
- err := db.InitEngine(ctx)
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
+ err := db.InitEngine(stdCtx)
if err != nil {
return err
}
@@ -163,7 +166,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
}
dumper.GlobalExcludeAbsPath(outFileName)
- if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") {
+ if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
log.Info("Skip dumping local repositories")
} else {
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
@@ -171,7 +174,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
fatal("Failed to include repositories: %v", err)
}
- if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") {
+ if ctx.IsSet("skip-lfs-data") && ctx.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")
@@ -186,12 +189,12 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
}
}
- if cmd.Bool("skip-db") {
+ if ctx.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 := cmd.String("tempdir")
+ tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
@@ -207,7 +210,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
}
}()
- targetDBType := cmd.String("database")
+ targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
@@ -228,7 +231,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
fatal("Failed to include specified app.ini: %v", err)
}
- if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") {
+ if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
log.Info("Skipping custom directory")
} else {
customDir, err := os.Stat(setting.CustomPath)
@@ -261,7 +264,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
excludes = append(excludes, opts.ProviderConfig)
}
- if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {
+ if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
excludes = append(excludes, setting.Indexer.RepoPath)
excludes = append(excludes, setting.Indexer.IssuePath)
}
@@ -276,7 +279,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
}
}
- if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") {
+ if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
log.Info("Skip dumping attachment data")
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat()
@@ -288,7 +291,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
fatal("Failed to dump attachments: %v", err)
}
- if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") {
+ if ctx.IsSet("skip-package-data") && ctx.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")
@@ -305,7 +308,7 @@ func runDump(ctx context.Context, cmd *cli.Command) 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 cmd.IsSet("skip-log") && cmd.Bool("skip-log") {
+ if ctx.IsSet("skip-log") && ctx.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 a75b2d1b94..3a24cf6c5f 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/v3"
+ "github.com/urfave/cli/v2"
)
// CmdDumpRepository represents the available dump repository sub-command.
@@ -79,13 +79,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
- setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
+func runDumpRepository(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
- setting.DisableLoggerInit()
- setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
-
- if err := initDB(ctx); err != nil {
+ if err := initDB(stdCtx); err != nil {
return err
}
@@ -102,8 +100,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
var (
serviceType structs.GitServiceType
- cloneAddr = cmd.String("clone_addr")
- serviceStr = cmd.String("git_service")
+ cloneAddr = ctx.String("clone_addr")
+ serviceStr = ctx.String("git_service")
)
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
@@ -121,13 +119,13 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
opts := base.MigrateOptions{
GitServiceType: serviceType,
CloneAddr: cloneAddr,
- AuthUsername: cmd.String("auth_username"),
- AuthPassword: cmd.String("auth_password"),
- AuthToken: cmd.String("auth_token"),
- RepoName: cmd.String("repo_name"),
+ AuthUsername: ctx.String("auth_username"),
+ AuthPassword: ctx.String("auth_password"),
+ AuthToken: ctx.String("auth_token"),
+ RepoName: ctx.String("repo_name"),
}
- if len(cmd.String("units")) == 0 {
+ if len(ctx.String("units")) == 0 {
opts.Wiki = true
opts.Issues = true
opts.Milestones = true
@@ -137,8 +135,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
opts.PullRequests = true
opts.ReleaseAssets = true
} else {
- units := strings.SplitSeq(cmd.String("units"), ",")
- for unit := range units {
+ units := strings.Split(ctx.String("units"), ",")
+ for _, unit := range units {
switch strings.ToLower(strings.TrimSpace(unit)) {
case "":
continue
@@ -166,7 +164,7 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) 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 := cmd.String("repo_dir")
+ repoDir := ctx.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 {
@@ -181,7 +179,7 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
if err := migrations.DumpRepository(
context.Background(),
repoDir,
- cmd.String("owner_name"),
+ ctx.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 1908352453..9f03f7be7c 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"fmt"
"os"
@@ -20,7 +19,7 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// CmdEmbedded represents the available extract sub-command.
@@ -29,7 +28,7 @@ var (
Name: "embedded",
Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdList,
subcmdView,
subcmdExtract,
@@ -101,7 +100,7 @@ type assetFile struct {
path string
}
-func initEmbeddedExtractor(c *cli.Command) error {
+func initEmbeddedExtractor(c *cli.Context) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice())
@@ -116,31 +115,31 @@ func initEmbeddedExtractor(c *cli.Command) error {
return nil
}
-func runList(_ context.Context, c *cli.Command) error {
+func runList(c *cli.Context) 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(_ context.Context, c *cli.Command) error {
+func runView(c *cli.Context) 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(_ context.Context, c *cli.Command) error {
+func runExtract(c *cli.Context) 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.Command) error {
+func runListDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -152,7 +151,7 @@ func runListDo(c *cli.Command) error {
return nil
}
-func runViewDo(c *cli.Command) error {
+func runViewDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -175,7 +174,7 @@ func runViewDo(c *cli.Command) error {
return nil
}
-func runExtractDo(c *cli.Command) error {
+func runExtractDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -217,7 +216,7 @@ func runExtractDo(c *cli.Command) error {
for _, a := range matchedAssetFiles {
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
// Non-fatal error
- _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
+ fmt.Fprintf(os.Stderr, "%s: %v", a.path, err)
}
}
@@ -272,7 +271,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil
}
-func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
+func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true)
if err != nil {
@@ -295,14 +294,16 @@ func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string,
}
}
-func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
+func compileCollectPatterns(args []string) ([]glob.Glob, error) {
if len(args) == 0 {
args = []string{"**"}
}
pat := make([]glob.Glob, len(args))
for i := range args {
- if pat[i], err = glob.Compile(args[i], '/'); err != nil {
- return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
+ 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
}
}
return pat, nil
diff --git a/cmd/generate.go b/cmd/generate.go
index cf491604ef..90b32ecaf0 100644
--- a/cmd/generate.go
+++ b/cmd/generate.go
@@ -5,14 +5,13 @@
package cmd
import (
- "context"
"fmt"
"os"
"code.gitea.io/gitea/modules/generate"
"github.com/mattn/go-isatty"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -20,7 +19,7 @@ var (
CmdGenerate = &cli.Command{
Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdSecret,
},
}
@@ -28,7 +27,7 @@ var (
subcmdSecret = &cli.Command{
Name: "secret",
Usage: "Generate a secret token",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
@@ -55,7 +54,7 @@ var (
}
)
-func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
+func runGenerateInternalToken(c *cli.Context) error {
internalToken, err := generate.NewInternalToken()
if err != nil {
return err
@@ -70,7 +69,7 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
return nil
}
-func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
+func runGenerateLfsJwtSecret(c *cli.Context) error {
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
if err != nil {
return err
@@ -85,7 +84,7 @@ func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
return nil
}
-func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
+func runGenerateSecretKey(c *cli.Context) error {
secretKey, err := generate.NewSecretKey()
if err != nil {
return err
diff --git a/cmd/hook.go b/cmd/hook.go
index 2ce272b411..578380ab40 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/v3"
+ "github.com/urfave/cli/v2"
)
const (
- hookBatchSize = 500
+ hookBatchSize = 30
)
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),
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookPostReceive,
@@ -161,10 +161,12 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil
}
-func runHookPreReceive(ctx context.Context, c *cli.Command) error {
+func runHookPreReceive(c *cli.Context) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
+ ctx, cancel := installSignals()
+ defer cancel()
setup(ctx, c.Bool("debug"))
@@ -290,7 +292,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(_ context.Context, c *cli.Command) error {
+func runHookUpdate(c *cli.Context) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
@@ -307,12 +309,15 @@ func runHookUpdate(_ context.Context, c *cli.Command) error {
return nil
}
-func runHookPostReceive(ctx context.Context, c *cli.Command) error {
+func runHookPostReceive(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
// First of all run update-server-info no matter what
- if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil {
- return fmt.Errorf("failed to call 'git update-server-info': %w", err)
+ if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(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
@@ -480,7 +485,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 := range pushCount {
+ for idx := 0; idx < pushCount; idx++ {
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
kv := strings.SplitN(opt, "=", 2)
if len(kv) == 2 {
@@ -491,7 +496,10 @@ func pushOptions() map[string]string {
return opts
}
-func runHookProcReceive(ctx context.Context, c *cli.Command) error {
+func runHookProcReceive(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
@@ -732,7 +740,7 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType)
// read prefix
lengthBytes := make([]byte, 4)
- for i := range 4 {
+ for i := 0; i < 4; i++ {
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 86cd4834f2..91f24ff2b4 100644
--- a/cmd/hook_test.go
+++ b/cmd/hook_test.go
@@ -6,6 +6,7 @@ package cmd
import (
"bufio"
"bytes"
+ "context"
"strings"
"testing"
@@ -14,7 +15,7 @@ import (
func TestPktLine(t *testing.T) {
// test read
- ctx := t.Context()
+ ctx := context.Background()
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 8710756a81..7fdbe16119 100644
--- a/cmd/keys.go
+++ b/cmd/keys.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"fmt"
"strings"
@@ -12,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// CmdKeys represents the available keys sub-command
@@ -50,7 +49,7 @@ var CmdKeys = &cli.Command{
},
}
-func runKeys(ctx context.Context, c *cli.Command) error {
+func runKeys(c *cli.Context) error {
if !c.IsSet("username") {
return errors.New("No username provided")
}
@@ -69,6 +68,9 @@ func runKeys(ctx context.Context, c *cli.Command) 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)
@@ -76,6 +78,6 @@ func runKeys(ctx context.Context, c *cli.Command) error {
if extra.Error != nil {
return extra.Error
}
- _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
+ _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
return nil
}
diff --git a/cmd/mailer.go b/cmd/mailer.go
index 72bd8e5601..0c5f2c8c8d 100644
--- a/cmd/mailer.go
+++ b/cmd/mailer.go
@@ -4,18 +4,24 @@
package cmd
import (
- "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
-func runSendMail(ctx context.Context, c *cli.Command) error {
+func runSendMail(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
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 3b8a8a9311..fd648946ef 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"fmt"
"os"
"strings"
@@ -12,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// cmdHelp is our own help subcommand with more information
@@ -23,18 +22,18 @@ func cmdHelp() *cli.Command {
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
- Action: func(ctx context.Context, c *cli.Command) (err error) {
- lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea
+ Action: func(c *cli.Context) (err error) {
+ lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
targetCmdIdx := 0
- if c.Name == "help" {
+ if c.Command.Name == "help" {
targetCmdIdx = 1
}
- if lineage[targetCmdIdx] != lineage[targetCmdIdx].Root() {
- err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1] /* parent cmd */, lineage[targetCmdIdx].Name /* sub cmd */)
+ if lineage[targetCmdIdx+1].Command != nil {
+ err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
} else {
err = cli.ShowAppHelp(c)
}
- _, _ = fmt.Fprintf(c.Root().Writer, `
+ _, _ = fmt.Fprintf(c.App.Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
@@ -75,25 +74,25 @@ func appGlobalFlags() []cli.Flag {
}
}
-func prepareSubcommandWithGlobalFlags(command *cli.Command) {
- command.Flags = append(append([]cli.Flag{}, appGlobalFlags()...), command.Flags...)
+func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
+ command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
- command.Commands = append(command.Commands, cmdHelp())
+ command.Subcommands = append(command.Subcommands, cmdHelp())
}
- for i := range command.Commands {
- prepareSubcommandWithGlobalFlags(command.Commands[i])
+ for i := range command.Subcommands {
+ prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
}
}
// 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(context.Context, *cli.Command) error {
- return func(ctx context.Context, cmd *cli.Command) error {
+func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
+ return func(ctx *cli.Context) error {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
- for _, curCtx := range cmd.Lineage() {
+ for _, curCtx := range ctx.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
@@ -105,11 +104,11 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(context.Context, *
}
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
- if cmd.Bool("help") || action == nil {
+ if ctx.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
- return cmdHelp().Action(ctx, cmd)
+ return cmdHelp().Action(ctx)
}
- return action(ctx, cmd)
+ return action(ctx)
}
}
@@ -118,13 +117,14 @@ type AppVersion struct {
Extra string
}
-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]]"
+func NewMainApp(appVer AppVersion) *cli.App {
+ app := cli.NewApp()
+ app.Name = "Gitea"
+ app.HelpName = "gitea"
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.EnableShellCompletion = true
+ app.EnableBashCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
@@ -147,33 +147,29 @@ func NewMainApp(appVer AppVersion) *cli.Command {
// 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, appGlobalFlags()...)
+ app.Flags = append(app.Flags, globalFlags...)
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 {
- prepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
+ prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
}
app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...)
- setting.InitGiteaEnvVars()
return app
}
-func RunMainApp(app *cli.Command, args ...string) error {
- ctx, cancel := installSignals()
- defer cancel()
- err := app.Run(ctx, args)
+func RunMainApp(app *cli.App, args ...string) error {
+ err := app.Run(args)
if err == nil {
return nil
}
diff --git a/cmd/main_test.go b/cmd/main_test.go
index 7dfa87a0ef..c182b44019 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -4,10 +4,9 @@
package cmd
import (
- "context"
- "errors"
"fmt"
"io"
+ "os"
"path/filepath"
"strings"
"testing"
@@ -17,7 +16,7 @@ import (
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
func TestMain(m *testing.M) {
@@ -28,10 +27,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
-func newTestApp(testCmdAction cli.ActionFunc) *cli.Command {
+func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
- prepareSubcommandWithGlobalFlags(testCmd)
+ prepareSubcommandWithConfig(testCmd, appGlobalFlags())
app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name
return app
@@ -43,7 +42,7 @@ type runResult struct {
ExitCode int
}
-func runTestApp(app *cli.Command, args ...string) (runResult, error) {
+func runTestApp(app *cli.App, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
@@ -66,7 +65,7 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)"
- cli.RootCommandHelpTemplate = "(app help template)"
+ cli.AppHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
@@ -110,50 +109,70 @@ func TestCliCmd(t *testing.T) {
},
}
- for _, c := range cases {
- 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)
+ 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)
}
- 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)
- })
+ }
+ }
+ 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)
}
}
func TestCliCmdError(t *testing.T) {
- app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") })
+ app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Empty(t, r.Stdout)
+ assert.Equal(t, "", r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
- app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) })
+ app = newTestApp(func(ctx *cli.Context) 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.Empty(t, r.Stdout)
+ assert.Equal(t, "", r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
- app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
+ app = newTestApp(func(ctx *cli.Context) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Empty(t, r.Stdout)
- assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
+ 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 ....
- app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
+ app = newTestApp(func(ctx *cli.Context) 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.Empty(t, r.Stdout)
- assert.Empty(t, r.Stderr)
+ assert.Equal(t, "", r.Stdout)
+ assert.Equal(t, "", r.Stderr)
}
diff --git a/cmd/manager.go b/cmd/manager.go
index f0935ea065..bd2da8edc7 100644
--- a/cmd/manager.go
+++ b/cmd/manager.go
@@ -4,13 +4,12 @@
package cmd
import (
- "context"
"os"
"time"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -19,7 +18,7 @@ var (
Name: "manager",
Usage: "Manage the running gitea process",
Description: "This is a command for managing the running gitea process",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
subcmdShutdown,
subcmdRestart,
subcmdReloadTemplates,
@@ -109,31 +108,46 @@ var (
}
)
-func runShutdown(ctx context.Context, c *cli.Command) error {
+func runShutdown(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
extra := private.Shutdown(ctx)
return handleCliResponseExtra(extra)
}
-func runRestart(ctx context.Context, c *cli.Command) error {
+func runRestart(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
extra := private.Restart(ctx)
return handleCliResponseExtra(extra)
}
-func runReloadTemplates(ctx context.Context, c *cli.Command) error {
+func runReloadTemplates(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
extra := private.ReloadTemplates(ctx)
return handleCliResponseExtra(extra)
}
-func runFlushQueues(ctx context.Context, c *cli.Command) error {
+func runFlushQueues(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
return handleCliResponseExtra(extra)
}
-func runProcesses(ctx context.Context, c *cli.Command) error {
+func runProcesses(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
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 ac29e7d3e5..c2ae25ec57 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"errors"
"fmt"
"os"
@@ -12,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
var (
@@ -61,7 +60,7 @@ var (
subcmdLogging = &cli.Command{
Name: "logging",
Usage: "Adjust logging commands",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
{
Name: "pause",
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
@@ -105,7 +104,7 @@ var (
}, {
Name: "add",
Usage: "Add a logger",
- Commands: []*cli.Command{
+ Subcommands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
@@ -119,6 +118,7 @@ var (
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
+ Value: true,
},
&cli.Int64Flag{
Name: "max-size",
@@ -129,6 +129,7 @@ var (
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
+ Value: true,
},
&cli.IntFlag{
Name: "max-days",
@@ -139,6 +140,7 @@ var (
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
+ Value: true,
},
&cli.IntFlag{
Name: "compression-level",
@@ -193,7 +195,10 @@ var (
}
)
-func runRemoveLogger(ctx context.Context, c *cli.Command) error {
+func runRemoveLogger(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
logger := c.String("logger")
if len(logger) == 0 {
@@ -205,7 +210,10 @@ func runRemoveLogger(ctx context.Context, c *cli.Command) error {
return handleCliResponseExtra(extra)
}
-func runAddConnLogger(ctx context.Context, c *cli.Command) error {
+func runAddConnLogger(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "conn"
@@ -229,10 +237,13 @@ func runAddConnLogger(ctx context.Context, c *cli.Command) error {
if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
}
- return commonAddLogger(ctx, c, mode, vals)
+ return commonAddLogger(c, mode, vals)
}
-func runAddFileLogger(ctx context.Context, c *cli.Command) error {
+func runAddFileLogger(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "file"
@@ -259,10 +270,10 @@ func runAddFileLogger(ctx context.Context, c *cli.Command) error {
if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level")
}
- return commonAddLogger(ctx, c, mode, vals)
+ return commonAddLogger(c, mode, vals)
}
-func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
+func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String()
}
@@ -289,33 +300,46 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[
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(ctx context.Context, c *cli.Command) error {
+func runPauseLogging(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
userMsg := private.PauseLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runResumeLogging(ctx context.Context, c *cli.Command) error {
+func runResumeLogging(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
userMsg := private.ResumeLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
+func runReleaseReopenLogging(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setup(ctx, c.Bool("debug"))
userMsg := private.ReleaseReopenLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runSetLogSQL(ctx context.Context, c *cli.Command) error {
+func runSetLogSQL(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
setup(ctx, c.Bool("debug"))
extra := private.SetLogSQL(ctx, !c.Bool("off"))
diff --git a/cmd/migrate.go b/cmd/migrate.go
index e24dc9e572..459805a76d 100644
--- a/cmd/migrate.go
+++ b/cmd/migrate.go
@@ -7,11 +7,11 @@ 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/v3"
+ "github.com/urfave/cli/v2"
)
// CmdMigrate represents the available migrate sub-command.
@@ -22,8 +22,11 @@ var CmdMigrate = &cli.Command{
Action: runMigrate,
}
-func runMigrate(ctx context.Context, c *cli.Command) error {
- if err := initDB(ctx); err != nil {
+func runMigrate(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(stdCtx); err != nil {
return err
}
@@ -33,7 +36,7 @@ func runMigrate(ctx context.Context, c *cli.Command) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), migrations.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 2c63e15f50..6ece4bf661 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -13,6 +13,7 @@ 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"
@@ -20,9 +21,8 @@ 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/v3"
+ "github.com/urfave/cli/v2"
)
// 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 == actions_model.ArtifactStatusExpired {
+ if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
return nil
}
@@ -213,8 +213,11 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
})
}
-func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
- if err := initDB(ctx); err != nil {
+func runMigrateStorage(ctx *cli.Context) error {
+ stdCtx, cancel := installSignals()
+ defer cancel()
+
+ if err := initDB(stdCtx); err != nil {
return err
}
@@ -224,7 +227,7 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
@@ -235,51 +238,51 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
var dstStorage storage.ObjectStorage
var err error
- switch strings.ToLower(cmd.String("storage")) {
+ switch strings.ToLower(ctx.String("storage")) {
case "":
fallthrough
case string(setting.LocalStorageType):
- p := cmd.String("path")
+ p := ctx.String("path")
if p == "" {
log.Fatal("Path must be given when storage is local")
return nil
}
dstStorage, err = storage.NewLocalStorage(
- ctx,
+ stdCtx,
&setting.Storage{
Path: p,
})
case string(setting.MinioStorageType):
dstStorage, err = storage.NewMinioStorage(
- ctx,
+ stdCtx,
&setting.Storage{
MinioConfig: setting.MinioStorageConfig{
- 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"),
+ 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"),
},
})
case string(setting.AzureBlobStorageType):
dstStorage, err = storage.NewAzureBlobStorage(
- ctx,
+ stdCtx,
&setting.Storage{
AzureBlobConfig: setting.AzureBlobStorageConfig{
- 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"),
+ 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"),
},
})
default:
- return fmt.Errorf("unsupported storage type: %s", cmd.String("storage"))
+ return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
}
if err != nil {
return err
@@ -296,14 +299,14 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
"actions-artifacts": migrateActionsArtifacts,
}
- tp := strings.ToLower(cmd.String("type"))
+ tp := strings.ToLower(ctx.String("type"))
if m, ok := migratedMethods[tp]; ok {
- if err := m(ctx, dstStorage); err != nil {
+ if err := m(stdCtx, 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", cmd.String("type"))
+ return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
}
diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go
index 6817867e28..5d8c867993 100644
--- a/cmd/migrate_storage_test.go
+++ b/cmd/migrate_storage_test.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"os"
"strings"
"testing"
@@ -52,7 +53,7 @@ func TestMigratePackages(t *testing.T) {
assert.NotNil(t, v)
assert.NotNil(t, f)
- ctx := t.Context()
+ ctx := context.Background()
p := t.TempDir()
@@ -69,6 +70,6 @@ func TestMigratePackages(t *testing.T) {
entries, err := os.ReadDir(p)
assert.NoError(t, err)
assert.Len(t, entries, 2)
- assert.Equal(t, "01", entries[0].Name())
- assert.Equal(t, "tmp", entries[1].Name())
+ assert.EqualValues(t, "01", entries[0].Name())
+ assert.EqualValues(t, "tmp", entries[1].Name())
}
diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go
index c61f5a582e..37b32aa304 100644
--- a/cmd/restore_repo.go
+++ b/cmd/restore_repo.go
@@ -4,13 +4,12 @@
package cmd
import (
- "context"
"strings"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
// CmdRestoreRepository represents the available restore a repository sub-command.
@@ -49,7 +48,10 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runRestoreRepository(ctx context.Context, c *cli.Command) error {
+func runRestoreRepository(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
setting.MustInstalled()
var units []string
if s := c.String("units"); s != "" {
diff --git a/cmd/serv.go b/cmd/serv.go
index 8c6001e727..d2271b68d2 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "regexp"
"strconv"
"strings"
"time"
@@ -19,7 +20,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/models/repo"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
@@ -33,7 +34,15 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote"
- "github.com/urfave/cli/v3"
+ "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"
)
// CmdServ represents the available serv sub-command.
@@ -69,6 +78,22 @@ 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 {
@@ -79,10 +104,7 @@ 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, "")
- // 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:")
+ _, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
if logMsgFmt != "" {
logMsg := fmt.Sprintf(logMsgFmt, args...)
@@ -114,20 +136,19 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
switch verb {
- case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
+ case verbUploadPack, verbUploadArchive:
return perm.AccessModeRead
- case git.CmdVerbReceivePack:
+ case verbReceivePack:
return perm.AccessModeWrite
- case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
+ case verbLfsAuthenticate, verbLfsTransfer:
switch lfsVerb {
- case git.CmdSubVerbLfsUpload:
+ case "upload":
return perm.AccessModeWrite
- case git.CmdSubVerbLfsDownload:
+ case "download":
return perm.AccessModeRead
}
}
// should be unreachable
- setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
@@ -149,10 +170,13 @@ 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 "Bearer " + tokenString, nil
+ return fmt.Sprintf("Bearer %s", tokenString), nil
}
-func runServ(ctx context.Context, c *cli.Command) error {
+func runServ(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
// FIXME: This needs to internationalised
setup(ctx, c.Bool("debug"))
@@ -203,37 +227,41 @@ func runServ(ctx context.Context, c *cli.Command) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
}
- sshCmdArgs, err := shellquote.Split(cmd)
+ words, err := shellquote.Split(cmd)
if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
}
- if len(sshCmdArgs) < 2 {
+ if len(words) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
- fmt.Print(`{"type":"agit","version":1}`)
+ fmt.Print(`{"type":"gitea","version":1}`)
return nil
}
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
- repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
- repoPathFields := strings.SplitN(repoPath, "/", 2)
- if len(repoPathFields) != 2 {
+ verb := words[0]
+ repoPath := strings.TrimPrefix(words[1], "/")
+
+ var lfsVerb string
+
+ rr := strings.SplitN(repoPath, "/", 2)
+ if len(rr) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
}
- username := repoPathFields[0]
- reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
+ username := rr[0]
+ reponame := strings.TrimSuffix(rr[1], ".git")
// LowerCase and trim the repoPath as that's how they are stored.
// This should be done after splitting the repoPath into username and reponame
// so that username and reponame are not affected.
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
- if !repo.IsValidSSHAccessRepoName(reponame) {
+ if alphaDashDotPattern.MatchString(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
}
@@ -255,23 +283,22 @@ func runServ(ctx context.Context, c *cli.Command) error {
}()
}
- verb, lfsVerb := sshCmdArgs[0], ""
- if !git.IsAllowedVerbForServe(verb) {
+ 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 {
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)
@@ -280,7 +307,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
}
// LFS SSH protocol
- if verb == git.CmdVerbLfsTransfer {
+ if verb == verbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
@@ -289,7 +316,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
}
// LFS token authentication
- if verb == git.CmdVerbLfsAuthenticate {
+ if verb == verbLfsAuthenticate {
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)
@@ -342,9 +369,9 @@ func runServ(ctx context.Context, c *cli.Command) 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+"="+strconv.Itoa(0),
- repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10),
- repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10),
+ repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
+ repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
+ repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
repo_module.EnvAppURL+"="+setting.AppURL,
)
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
diff --git a/cmd/web.go b/cmd/web.go
index 61ee3cbc20..f8217758e5 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -18,17 +18,15 @@ import (
"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/v3"
+ "github.com/urfave/cli/v2"
)
// PIDFile could be set from build tag
@@ -130,19 +128,19 @@ func showWebStartupMessage(msg string) {
}
}
-func serveInstall(cmd *cli.Command) error {
+func serveInstall(ctx *cli.Context) error {
showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
// Flag for port number in case first time run conflict
- if cmd.IsSet("port") {
- if err := setPort(cmd.String("port")); err != nil {
+ if ctx.IsSet("port") {
+ if err := setPort(ctx.String("port")); err != nil {
return err
}
}
- if cmd.IsSet("install-port") {
- if err := setPort(cmd.String("install-port")); err != nil {
+ if ctx.IsSet("install-port") {
+ if err := setPort(ctx.String("install-port")); err != nil {
return err
}
}
@@ -163,7 +161,7 @@ func serveInstall(cmd *cli.Command) error {
return nil
}
-func serveInstalled(c *cli.Command) error {
+func serveInstalled(ctx *cli.Context) error {
setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings()
setting.MustInstalled()
@@ -213,19 +211,13 @@ func serveInstalled(c *cli.Command) 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 c.IsSet("port") {
- if err := setPort(c.String("port")); err != nil {
+ if ctx.IsSet("port") {
+ if err := setPort(ctx.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)
@@ -244,17 +236,13 @@ func servePprof() {
finished()
}
-func runWeb(_ context.Context, cmd *cli.Command) error {
+func runWeb(ctx *cli.Context) 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()
@@ -266,12 +254,12 @@ func runWeb(_ context.Context, cmd *cli.Command) error {
}
// Set pid file setting
- if cmd.IsSet("pid") {
- createPIDFile(cmd.String("pid"))
+ if ctx.IsSet("pid") {
+ createPIDFile(ctx.String("pid"))
}
if !setting.InstallLock {
- if err := serveInstall(cmd); err != nil {
+ if err := serveInstall(ctx); err != nil {
return err
}
} else {
@@ -282,7 +270,7 @@ func runWeb(_ context.Context, cmd *cli.Command) error {
go servePprof()
}
- return serveInstalled(cmd)
+ return serveInstalled(ctx)
}
func setPort(port string) error {
diff --git a/cmd/web_acme.go b/cmd/web_acme.go
index 5f7a308334..5daf0f55f2 100644
--- a/cmd/web_acme.go
+++ b/cmd/web_acme.go
@@ -16,7 +16,6 @@ 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"
)
@@ -55,6 +54,10 @@ func runACME(listenAddr string, m http.Handler) error {
altTLSALPNPort = p
}
+ // 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"
+ certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
+ magic := certmagic.NewDefault()
// Try to use private CA root if provided, otherwise defaults to system's trust
var certPool *x509.CertPool
if setting.AcmeCARoot != "" {
@@ -64,20 +67,8 @@ func runACME(listenAddr string, m http.Handler) error {
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
}
}
- // 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,
-
+ myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
+ CA: setting.AcmeURL,
TrustedRoots: certPool,
Email: setting.AcmeEmail,
Agreed: setting.AcmeTOS,
@@ -86,10 +77,8 @@ 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
@@ -136,7 +125,7 @@ func runACME(listenAddr string, m http.Handler) error {
}
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet && r.Method != http.MethodHead {
+ if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go
index 5e06d2c216..996537be3b 100644
--- a/cmd/web_graceful.go
+++ b/cmd/web_graceful.go
@@ -23,6 +23,12 @@ 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 2052295fb1..eb19437445 100644
--- a/contrib/backport/backport.go
+++ b/contrib/backport/backport.go
@@ -1,30 +1,31 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo // use of print functions is allowed in cli
+//nolint:forbidigo
package main
import (
"context"
- "errors"
"fmt"
"log"
"net/http"
"os"
"os/exec"
+ "os/signal"
"path"
"strconv"
"strings"
+ "syscall"
- "github.com/google/go-github/v71/github"
- "github.com/urfave/cli/v3"
+ "github.com/google/go-github/v61/github"
+ "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
const defaultVersion = "v1.18" // to backport to
func main() {
- app := &cli.Command{}
+ app := cli.NewApp()
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`
@@ -89,7 +90,7 @@ func main() {
Usage: "Set this flag to continue from a git cherry-pick that has broken",
},
}
- cli.RootCommandHelpTemplate = `NAME:
+ cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
@@ -103,12 +104,16 @@ OPTIONS:
`
app.Action = runBackport
- if err := app.Run(context.Background(), os.Args); err != nil {
+
+ if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
}
}
-func runBackport(ctx context.Context, c *cli.Command) error {
+func runBackport(c *cli.Context) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+
continuing := c.Bool("continue")
var pr string
@@ -153,7 +158,7 @@ func runBackport(ctx context.Context, c *cli.Command) error {
args := c.Args().Slice()
if len(args) == 0 && pr == "" {
- return errors.New("no PR number provided\nProvide a PR number to backport")
+ return fmt.Errorf("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)
}
@@ -337,8 +342,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.SplitSeq(string(out), "\n")
- for line := range lines {
+ lines := strings.Split(string(out), "\n")
+ for _, line := range lines {
fields := strings.Split(line, "\t")
name, remote := fields[0], fields[1]
// only look at pushers
@@ -356,12 +361,12 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
if !strings.Contains(remote, forkUser) {
continue
}
- 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
+ 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/")
} else if forkUser == "" {
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
}
@@ -454,3 +459,25 @@ 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 5eb576c6fe..a7d7a6d293 100644
--- a/contrib/environment-to-ini/environment-to-ini.go
+++ b/contrib/environment-to-ini/environment-to-ini.go
@@ -4,17 +4,16 @@
package main
import (
- "context"
"os"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v3"
+ "github.com/urfave/cli/v2"
)
func main() {
- app := cli.Command{}
+ app := cli.NewApp()
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
@@ -73,13 +72,13 @@ func main() {
},
}
app.Action = runEnvironmentToIni
- err := app.Run(context.Background(), os.Args)
+ err := app.Run(os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
}
}
-func runEnvironmentToIni(_ context.Context, c *cli.Command) error {
+func runEnvironmentToIni(c *cli.Context) 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 aa2fcee765..6896b073e1 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -59,22 +59,27 @@ RUN_USER = ; git
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; The protocol the server listens on. One of "http", "https", "http+unix", "fcgi" or "fcgi+unix".
+;; The protocol the server listens on. One of 'http', 'https', 'http+unix', 'fcgi' or 'fcgi+unix'. Defaults to 'http'
+;; Note: Value must be lowercase.
;PROTOCOL = http
;;
-;; Set the domain for the server.
+;; 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
;DOMAIN = localhost
;;
-;; 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
+;; Overwrite the automatically generated public URL. Necessary for proxies and docker.
+;ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
;;
;; 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!!!
@@ -84,25 +89,13 @@ 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 for "http" or "https" protocol. Leave empty when using a unix socket.
+;; The port to listen on. 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
@@ -110,8 +103,8 @@ RUN_USER = ; git
;REDIRECT_OTHER_PORT = false
;PORT_TO_REDIRECT = 80
;;
-;; expect PROXY protocol header on connections to https redirector, defaults to USE_PROXY_PROTOCOL
-;REDIRECTOR_USE_PROXY_PROTOCOL =
+;; expect PROXY protocol header on connections to https redirector.
+;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
;; Minimum and maximum supported TLS versions
;SSL_MIN_VERSION=TLSv1.2
;SSL_MAX_VERSION=
@@ -135,14 +128,13 @@ 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}://{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 =
+;; 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/
;;
-;; When making local connections pass the PROXY protocol header, defaults to USE_PROXY_PROTOCOL
-;LOCAL_USE_PROXY_PROTOCOL =
+;; When making local connections pass the PROXY protocol header.
+;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
;;
;; Disable SSH feature when not available
;DISABLE_SSH = false
@@ -154,17 +146,13 @@ 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 =
+;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
;;
-;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
-;SSH_DOMAIN =
+;; Domain name to be exposed in clone URL
+;SSH_DOMAIN = %(DOMAIN)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 =
+;; SSH username displayed in clone URLs.
+;SSH_USER = %(BUILTIN_SSH_SERVER_USER)s
;;
;; The network interface the builtin SSH server should listen on
;SSH_LISTEN_HOST =
@@ -172,8 +160,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, defaults to SSH_PORT
-;SSH_LISTEN_PORT =
+;; The port number the builtin SSH server should listen on
+;SSH_LISTEN_PORT = %(SSH_PORT)s
;;
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
;SSH_ROOT_PATH =
@@ -186,19 +174,30 @@ 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 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 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 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
+;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
;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
;;
@@ -289,9 +288,6 @@ 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
;;
@@ -520,10 +516,6 @@ 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 =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -590,7 +582,7 @@ ENABLED = true
[log]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Root path for the log files - defaults to "{AppWorkPath}/log"
+;; Root path for the log files - defaults to %(GITEA_WORK_DIR)/log
;ROOT_PATH =
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -690,8 +682,8 @@ LEVEL = Info
;; The path of git executable. If empty, Gitea searches through the PATH environment.
;PATH =
;;
-;; The HOME directory for Git, defaults to "{APP_DATA_PATH}/home"
-;HOME_PATH =
+;; The HOME directory for Git
+;HOME_PATH = %(APP_DATA_PATH)s/home
;;
;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false
@@ -782,9 +774,6 @@ 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
@@ -795,13 +784,10 @@ LEVEL = Info
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
;ENABLE_BASIC_AUTHENTICATION = true
;;
-;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 or passkey login methods if they are enabled.
+;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods.
;; 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.
@@ -946,29 +932,7 @@ 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -982,8 +946,8 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; 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 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 =
;;
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
@@ -1093,6 +1057,15 @@ 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]
@@ -1102,6 +1075,9 @@ 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 =
;;
@@ -1144,9 +1120,6 @@ 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
;;
@@ -1155,10 +1128,6 @@ 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1186,24 +1155,17 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; 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
+;; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
;; 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 and the signing format.
+;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
;; 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, git config --get user.email and git config --default openpgp --get gpg.format respectively and can only be overridden
+;; the results of git config --get user.name and git config --get user.email 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
@@ -1230,13 +1192,6 @@ 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 =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1327,9 +1282,6 @@ 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
@@ -1387,9 +1339,6 @@ 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]
@@ -1443,14 +1392,14 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; 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 =
+;; 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
;;
;; 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)
@@ -1464,11 +1413,6 @@ 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 =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1559,8 +1503,7 @@ 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.
-;; Relative paths will be made absolute against "APP_DATA_PATH"
-;DATADIR = queues/
+;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
;;
;; Default queue length before a channel queue will block
;LENGTH = 100000
@@ -1808,9 +1751,6 @@ 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2462,8 +2402,6 @@ 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2504,7 +2442,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits)
-;MERMAID_MAX_SOURCE_CHARACTERS = 50000
+;MERMAID_MAX_SOURCE_CHARACTERS = 5000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2625,6 +2563,9 @@ 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 3fa94ab0ec..1ebf5b73c8 100644
--- a/docker/manifest.rootless.tmpl
+++ b/docker/manifest.rootless.tmpl
@@ -22,8 +22,3 @@ 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 c68ca46dd8..08ccf61b57 100644
--- a/docker/manifest.tmpl
+++ b/docker/manifest.tmpl
@@ -22,8 +22,3 @@ 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 48e7d4b211..dbb3bafd35 100755
--- a/docker/root/etc/s6/openssh/setup
+++ b/docker/root/etc/s6/openssh/setup
@@ -31,21 +31,6 @@ 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/flake.lock b/flake.lock
index da3f19bbd2..1890b82dcf 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1731533236,
- "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "lastModified": 1726560853,
+ "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1747179050,
- "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
+ "lastModified": 1731139594,
+ "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
+ "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 1b930649d0..e3655b627e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -29,14 +29,9 @@
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 afe7c990e4..422952af9e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module code.gitea.io/gitea
-go 1.24.4
+go 1.23
// 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.24.4
godebug x509negativeserial=1
require (
- code.gitea.io/actions-proto-go v0.4.1
+ code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/gitea-vet v0.2.3
- code.gitea.io/sdk/gitea v0.21.0
+ code.gitea.io/sdk/gitea v0.19.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
- connectrpc.com/connect v1.18.1
+ connectrpc.com/connect v1.17.0
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,19 @@ 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.18.0
- github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
+ 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/go-ntlmssp v0.0.0-20221128193559-754e69321358
- github.com/ProtonMail/go-crypto v1.2.0
- github.com/PuerkitoBio/goquery v1.10.3
+ github.com/ProtonMail/go-crypto v1.1.4
+ github.com/PuerkitoBio/goquery v1.10.0
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.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/alecthomas/chroma/v2 v2.15.0
+ 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/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
- 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/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/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,35 +41,37 @@ 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.3
+ github.com/editorconfig/editorconfig-core-go/v2 v2.6.2
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.9.0
+ github.com/fsnotify/fsnotify v1.7.0
github.com/gliderlabs/ssh v0.3.8
- github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d
+ github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
- github.com/go-chi/chi/v5 v5.2.2
+ github.com/go-chi/chi/v5 v5.1.0
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.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-enry/go-enry/v2 v2.9.1
+ github.com/go-git/go-billy/v5 v5.6.1
+ github.com/go-git/go-git/v5 v5.13.1
+ github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-redsync/redsync/v4 v4.13.0
- github.com/go-sql-driver/mysql v1.9.2
- github.com/go-webauthn/webauthn v0.12.3
+ github.com/go-sql-driver/mysql v1.8.1
+ github.com/go-swagger/go-swagger v0.31.0
+ github.com/go-webauthn/webauthn v0.11.2
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
- github.com/golang-jwt/jwt/v5 v5.2.2
- github.com/google/go-github/v71 v71.0.0
+ github.com/golang-jwt/jwt/v5 v5.2.1
+ github.com/google/go-github/v61 v61.0.0
github.com/google/licenseclassifier/v2 v2.0.0
- github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416
+ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db
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
@@ -78,179 +79,217 @@ 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/klauspost/compress v1.18.0
- github.com/klauspost/cpuid/v2 v2.2.10
+ github.com/klauspost/compress v1.17.11
+ github.com/klauspost/cpuid/v2 v2.2.8
github.com/lib/pq v1.10.9
- github.com/markbates/goth v1.81.0
+ github.com/markbates/goth v1.80.0
github.com/mattn/go-isatty v0.0.20
- github.com/mattn/go-sqlite3 v1.14.28
- github.com/meilisearch/meilisearch-go v0.31.0
+ github.com/mattn/go-sqlite3 v1.14.24
+ github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27
- github.com/microsoft/go-mssqldb v1.8.0
- github.com/minio/minio-go/v7 v7.0.91
+ github.com/microsoft/go-mssqldb v1.7.2
+ github.com/minio/minio-go/v7 v7.0.80
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.63
- github.com/niklasfasching/go-org v1.8.0
+ github.com/niklasfasching/go-org v1.7.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
- github.com/opencontainers/image-spec v1.1.1
+ github.com/opencontainers/image-spec v1.1.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
- github.com/prometheus/client_golang v1.22.0
+ github.com/prometheus/client_golang v1.20.5
github.com/quasoft/websspi v1.1.2
- github.com/redis/go-redis/v9 v9.7.3
+ github.com/redis/go-redis/v9 v9.7.0
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
+ github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
github.com/stretchr/testify v1.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-docs/v3 v3.0.0-alpha6
- github.com/urfave/cli/v3 v3.3.3
- github.com/wneessen/go-mail v0.6.2
+ 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/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
- github.com/yuin/goldmark v1.7.10
+ github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
- 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
+ golang.org/x/crypto v0.32.0
+ golang.org/x/image v0.21.0
+ golang.org/x/net v0.34.0
+ golang.org/x/oauth2 v0.23.0
+ golang.org/x/sync v0.10.0
+ golang.org/x/sys v0.29.0
+ golang.org/x/text v0.21.0
+ golang.org/x/tools v0.29.0
+ google.golang.org/grpc v1.67.1
+ google.golang.org/protobuf v1.35.1
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
- mvdan.cc/xurls/v2 v2.6.0
+ mvdan.cc/xurls/v2 v2.5.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.6.0 // indirect
+ cloud.google.com/go/compute/metadata v0.5.2 // 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.11.1 // indirect
- github.com/DataDog/zstd v1.5.7 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
+ github.com/DataDog/zstd v1.5.6 // indirect
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver/v3 v3.3.0 // indirect
+ github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
+ github.com/RoaringBitmap/roaring v1.9.4 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
- github.com/andybalholm/cascadia v1.3.3 // indirect
+ github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // 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/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/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // 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/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/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.3.10 // indirect
+ github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // 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.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/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/boombuler/barcode v1.0.2 // indirect
- github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
+ github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/cloudflare/circl v1.6.1 // indirect
+ github.com/cloudflare/circl v1.5.0 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
- github.com/couchbase/gomemcached v0.3.3 // indirect
+ github.com/couchbase/gomemcached v0.3.2 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
- github.com/cyphar/filepath-securejoin v0.4.1 // indirect
+ github.com/cyphar/filepath-securejoin v0.3.6 // 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.5 // indirect
+ github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
- github.com/fxamacker/cbor/v2 v2.8.0 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
- github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect
- github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // 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-enry/go-oniguruma v1.2.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-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/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/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-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/golang/snappy v1.0.0 // indirect
+ github.com/golang/snappy v0.0.4 // 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.3 // indirect
+ github.com/google/go-tpm v0.9.1 // 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/libdns/libdns v1.0.0-beta.1 // indirect
- github.com/mailru/easyjson v0.9.0 // 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/markbates/going v1.0.3 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/mattn/go-shellwords v1.0.12 // indirect
- github.com/mholt/acmez/v3 v3.1.2 // indirect
- github.com/miekg/dns v1.1.65 // indirect
- github.com/minio/crc64nvme v1.0.1 // indirect
+ github.com/mholt/acmez/v2 v2.0.3 // indirect
+ github.com/miekg/dns v1.1.62 // 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/mmcloughlin/avo v0.6.0 // 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/pierrec/lz4/v4 v4.1.22 // indirect
- github.com/pjbgf/sha1cd v0.3.2 // 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.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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/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/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/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.1 // 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/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
@@ -258,25 +297,27 @@ 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.4.0 // indirect
+ go.etcd.io/bbolt v1.3.11 // indirect
+ go.mongodb.org/mongo-driver v1.17.1 // 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
- 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
+ golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/time v0.7.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // 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/nektos/act => gitea.com/gitea/act v0.261.6
+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
// 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
@@ -284,8 +325,6 @@ 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 2e7c51f747..e7f6b7c6d9 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,25 @@
-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=
+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=
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.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
-code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
+code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y=
+code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
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.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
-connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
+connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk=
+connectrpc.com/connect v1.17.0/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=
-gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY=
-gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
+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/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,46 +40,52 @@ 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.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/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/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.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/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/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.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
-github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
+github.com/DataDog/zstd v1.5.6/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.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/ProtonMail/go-crypto v1.1.4 h1:G5U5asvD5N/6/36oIw3k2bOfBn5XVcZrb7PBjzzKKoE=
+github.com/ProtonMail/go-crypto v1.1.4/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
+github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
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/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
-github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
+github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
+github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
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.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.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
-github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
+github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
+github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
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=
@@ -90,25 +96,27 @@ 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.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
-github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
+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/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/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/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-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/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=
@@ -116,20 +124,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.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
-github.com/bits-and-blooms/bitset v1.22.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/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.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/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_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-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/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/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=
@@ -138,8 +146,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.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/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/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=
@@ -150,43 +158,38 @@ 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.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
-github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
+github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
+github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM=
-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/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/v12 v12.2.0/go.mod h1:fdjwvCwWWwJW/EYTYGtAp3gBA0geCYGLcVTtJEZnY6A=
-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/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
+github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
github.com/blevesearch/zapx/v13 v13.2.0/go.mod h1:o5rAy/lRS5JpAbITdrOHBS/TugWYbkcYZTz6VfEinAQ=
-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/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
+github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCquiEKYh1xLFK9g=
-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/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
+github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
-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/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/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-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
-github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
+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/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.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/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/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
+github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
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=
@@ -201,16 +204,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.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
-github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+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/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.3 h1:D7qqXLO8wNa4pn5oE65lT3pA3IeStn4joT7/JgGXzKc=
-github.com/couchbase/gomemcached v0.3.3/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP9g3Wfhl1JJw68=
+github.com/couchbase/gomemcached v0.3.2 h1:08rxiOoNcv0x5LTxgcYhnx1aPvV7iEtfeyUgqsJyPk0=
+github.com/couchbase/gomemcached v0.3.2/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
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=
@@ -218,8 +221,8 @@ 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.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
-github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
+github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
+github.com/cyphar/filepath-securejoin v0.3.6/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=
@@ -238,19 +241,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.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
-github.com/dlclark/regexp2 v1.11.5/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/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.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
-github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
+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/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
-github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
-github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
+github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
+github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
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=
@@ -266,53 +269,80 @@ 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.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/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/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-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/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/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.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-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-chi/chi/v5 v5.0.1/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/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/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.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
-github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
+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-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-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.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-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
+github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
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.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
-github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
+github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
+github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
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.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
-github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
+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-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=
@@ -321,35 +351,38 @@ 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.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
-github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
+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-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-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/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/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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
-github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+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/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/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-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/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-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=
@@ -365,37 +398,33 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
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 v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
-github.com/golang/snappy v1.0.0/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/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-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-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-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.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
-github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
+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/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-20250422154841-e1f9c1950416 h1:1/qwHx8P72glDXdyCKesJ+/c40x71SY4q2avOxJ2iYQ=
-github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
+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/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=
@@ -408,6 +437,8 @@ 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=
@@ -416,8 +447,13 @@ 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=
@@ -429,10 +465,12 @@ 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=
@@ -457,6 +495,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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -472,12 +512,12 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
-github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+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/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.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
-github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+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/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=
@@ -496,47 +536,53 @@ 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 v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
-github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
+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/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.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/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/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-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/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/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.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/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/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.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc=
-github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go=
+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/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/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
+github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
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=
@@ -551,14 +597,18 @@ 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/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
-github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
+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/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=
@@ -575,16 +625,18 @@ 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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
-github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
+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/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
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.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/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.1 h1:Dh2GYdpJnO84lIw0LJwTFXjcNbasP/bklicSznyAaPI=
+github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s=
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=
@@ -595,25 +647,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.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/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/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.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
-github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
+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/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.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k=
-github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg=
+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/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=
@@ -622,13 +674,18 @@ 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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
-github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+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/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=
@@ -637,23 +694,37 @@ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLS
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.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
-github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
+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/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=
@@ -661,7 +732,6 @@ 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=
@@ -671,14 +741,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.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/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=
@@ -688,17 +761,17 @@ 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-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/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/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.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
-github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
+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/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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -711,6 +784,8 @@ 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=
@@ -719,8 +794,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
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.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
-github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
+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-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=
@@ -731,11 +806,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.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
-go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
+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.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
+go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
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=
@@ -745,8 +820,6 @@ 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=
@@ -754,18 +827,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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
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.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
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/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/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=
@@ -775,11 +848,12 @@ 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.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
-golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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=
@@ -789,30 +863,29 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
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.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.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/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
+golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-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=
@@ -836,30 +909,31 @@ 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.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.28.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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+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.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
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=
@@ -869,12 +943,11 @@ 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/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/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
+golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
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=
@@ -885,24 +958,24 @@ 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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
-golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
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-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/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/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.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
-google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+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=
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=
@@ -924,7 +997,6 @@ 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=
@@ -947,10 +1019,8 @@ 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.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=
+mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
+mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
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 2c25bac4e3..756c3e0f9b 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/v3"
+ "github.com/urfave/cli/v2"
)
// these flags will be set by the build flags
diff --git a/models/actions/artifact.go b/models/actions/artifact.go
index 757bd13acd..706eb2e43a 100644
--- a/models/actions/artifact.go
+++ b/models/actions/artifact.go
@@ -30,25 +30,6 @@ 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))
}
@@ -67,7 +48,7 @@ type ActionArtifact struct {
ContentEncoding string // The content encoding of the artifact
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
- Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
+ Status int64 `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
@@ -87,7 +68,7 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
RepoID: t.RepoID,
OwnerID: t.OwnerID,
CommitSHA: t.CommitSHA,
- Status: ArtifactStatusUploadPending,
+ Status: int64(ArtifactStatusUploadPending),
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
}
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
@@ -127,11 +108,10 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro
type FindArtifactsOptions struct {
db.ListOptions
- RepoID int64
- RunID int64
- ArtifactName string
- Status int
- FinalizedArtifactsV4 bool
+ RepoID int64
+ RunID int64
+ ArtifactName string
+ Status int
}
func (opts FindArtifactsOptions) ToOrders() string {
@@ -154,10 +134,6 @@ 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
}
@@ -196,18 +172,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: ArtifactStatusExpired})
+ _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(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: ArtifactStatusPendingDeletion})
+ _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(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: ArtifactStatusDeleted})
+ _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
return err
}
diff --git a/models/actions/run.go b/models/actions/run.go
index f0ab61b200..f40bc1eb3d 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -5,7 +5,6 @@ package actions
import (
"context"
- "errors"
"fmt"
"slices"
"strings"
@@ -16,7 +15,6 @@ 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"
@@ -90,7 +88,7 @@ func (run *ActionRun) RefLink() string {
if refName.IsPull() {
return run.Repo.Link() + "/pulls/" + refName.ShortName()
}
- return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
+ return git.RefURL(run.Repo.Link(), run.Ref)
}
// PrettyRef return #id for pull ref or ShortName for others
@@ -156,7 +154,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
}
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
- if run.Event.IsPullRequest() {
+ if run.Event == webhook_module.HookEventPullRequest || run.Event == webhook_module.HookEventPullRequestSync {
var payload api.PullRequestPayload
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
return nil, err
@@ -166,24 +164,12 @@ 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}),
@@ -208,7 +194,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
-func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) ([]*ActionRunJob, error) {
+func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) 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,
@@ -218,16 +204,14 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
Status: []Status{StatusRunning, StatusWaiting, StatusBlocked},
})
if err != nil {
- return nil, err
+ return err
}
// If there are no runs found, there's no need to proceed with cancellation, so return nil.
if total == 0 {
- return nil, nil
+ return 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.
@@ -235,7 +219,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
RunID: run.ID,
})
if err != nil {
- return cancelledJobs, err
+ return err
}
// Iterate over each job and attempt to cancel it.
@@ -254,29 +238,27 @@ 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 cancelledJobs, err
+ return 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 cancelledJobs, errors.New("job has changed, try again")
+ return fmt.Errorf("job has changed, try again")
}
- cancelledJobs = append(cancelledJobs, job)
// Continue with the next job.
continue
}
// If the job has an associated task, try to stop the task, effectively cancelling the job.
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
- return cancelledJobs, err
+ return err
}
- cancelledJobs = append(cancelledJobs, job)
}
}
// Return nil to indicate successful cancellation of all running and waiting jobs.
- return cancelledJobs, nil
+ return nil
}
// InsertRun inserts a run
@@ -293,7 +275,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return err
}
run.Index = index
- run.Title = util.EllipsisDisplayString(run.Title, 255)
+ run.Title, _ = util.SplitStringAtByteN(run.Title, 255)
if err := db.Insert(ctx, run); err != nil {
return err
@@ -326,7 +308,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
} else {
hasWaiting = true
}
- job.Name = util.EllipsisDisplayString(job.Name, 255)
+ job.Name, _ = util.SplitStringAtByteN(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{
RunID: run.ID,
RepoID: run.RepoID,
@@ -355,13 +337,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return committer.Commit()
}
-func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
+func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
var run ActionRun
- has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", runID, repoID).Get(&run)
+ has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
if err != nil {
return nil, err
} else if !has {
- return nil, fmt.Errorf("run with id %d: %w", runID, util.ErrNotExist)
+ return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist)
}
return &run, nil
@@ -420,22 +402,29 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
if len(cols) > 0 {
sess.Cols(cols...)
}
- run.Title = util.EllipsisDisplayString(run.Title, 255)
+ run.Title, _ = util.SplitStringAtByteN(run.Title, 255)
affected, err := sess.Update(run)
if err != nil {
return err
}
if affected == 0 {
- return errors.New("run has changed")
+ return fmt.Errorf("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 {
- setting.PanicInDevOrTesting("RepoID should not be 0")
+ run, err = GetRunByID(ctx, run.ID)
+ if err != nil {
+ return err
+ }
}
- if err = run.LoadRepo(ctx); err != nil {
- return err
+ if run.Repo == nil {
+ repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
+ if err != nil {
+ return err
+ }
+ run.Repo = repo
}
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 bad895036d..de4b6aab66 100644
--- a/models/actions/run_job.go
+++ b/models/actions/run_job.go
@@ -10,7 +10,6 @@ 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"
@@ -20,12 +19,11 @@ import (
// ActionRunJob represents a job of a run
type ActionRunJob struct {
ID int64
- RunID int64 `xorm:"index"`
- Run *ActionRun `xorm:"-"`
- RepoID int64 `xorm:"index"`
- Repo *repo_model.Repository `xorm:"-"`
- OwnerID int64 `xorm:"index"`
- CommitSHA string `xorm:"index"`
+ RunID int64 `xorm:"index"`
+ Run *ActionRun `xorm:"-"`
+ RepoID int64 `xorm:"index"`
+ OwnerID int64 `xorm:"index"`
+ CommitSHA string `xorm:"index"`
IsForkPullRequest bool
Name string `xorm:"VARCHAR(255)"`
Attempt int64
@@ -51,7 +49,7 @@ func (job *ActionRunJob) Duration() time.Duration {
func (job *ActionRunJob) LoadRun(ctx context.Context) error {
if job.Run == nil {
- run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
+ run, err := GetRunByID(ctx, job.RunID)
if err != nil {
return err
}
@@ -60,17 +58,6 @@ 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 {
@@ -96,7 +83,7 @@ func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
return &job, nil
}
-func GetRunJobsByRunID(ctx context.Context, runID int64) (ActionJobList, error) {
+func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error) {
var jobs []*ActionRunJob
if err := db.GetEngine(ctx).Where("run_id=?", runID).OrderBy("id").Find(&jobs); err != nil {
return nil, err
@@ -142,7 +129,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 := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
+ run, err := GetRunByID(ctx, job.RunID)
if err != nil {
return 0, err
}
@@ -185,10 +172,10 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
return StatusSuccess
case hasCancelled:
return StatusCancelled
- case hasRunning:
- return StatusRunning
case hasFailure:
return StatusFailure
+ case hasRunning:
+ return StatusRunning
case hasWaiting:
return StatusWaiting
case hasBlocked:
diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go
index 5f7bb62878..6c5d3b3252 100644
--- a/models/actions/run_job_list.go
+++ b/models/actions/run_job_list.go
@@ -7,7 +7,6 @@ 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"
@@ -22,33 +21,7 @@ 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 {
@@ -57,9 +30,15 @@ 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
}
@@ -80,31 +59,22 @@ type FindRunJobOptions struct {
func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RunID > 0 {
- cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
+ cond = cond.And(builder.Eq{"run_id": opts.RunID})
}
if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
+ cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
+ }
+ if opts.OwnerID > 0 {
+ cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.CommitSHA != "" {
- cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
+ cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
}
if len(opts.Statuses) > 0 {
- cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
+ cond = cond.And(builder.In("status", opts.Statuses))
}
if opts.UpdatedBefore > 0 {
- cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
+ cond = cond.And(builder.Lt{"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 2a5eb00a6f..523d38327e 100644
--- a/models/actions/run_job_status_test.go
+++ b/models/actions/run_job_status_test.go
@@ -58,14 +58,14 @@ func TestAggregateJobStatus(t *testing.T) {
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
- // 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.
+ // failure with other status, fail fast
+ // Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
{[]Status{StatusFailure}, StatusFailure},
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
- {[]Status{StatusFailure, StatusRunning}, StatusRunning},
+ {[]Status{StatusFailure, StatusRunning}, StatusFailure},
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
// skipped with other status
diff --git a/models/actions/run_list.go b/models/actions/run_list.go
index 12c55e538e..4046c7d369 100644
--- a/models/actions/run_list.go
+++ b/models/actions/run_list.go
@@ -10,7 +10,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/translation"
webhook_module "code.gitea.io/gitea/modules/webhook"
"xorm.io/builder"
@@ -72,50 +71,39 @@ 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{"`action_run`.repo_id": opts.RepoID})
+ cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
+ }
+ if opts.OwnerID > 0 {
+ cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.WorkflowID != "" {
- cond = cond.And(builder.Eq{"`action_run`.workflow_id": opts.WorkflowID})
+ cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID})
}
if opts.TriggerUserID > 0 {
- cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
+ cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
}
if opts.Approved {
- cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
+ cond = cond.And(builder.Gt{"approved_by": 0})
}
if len(opts.Status) > 0 {
- cond = cond.And(builder.In("`action_run`.status", opts.Status))
+ cond = cond.And(builder.In("status", opts.Status))
}
if opts.Ref != "" {
- cond = cond.And(builder.Eq{"`action_run`.ref": opts.Ref})
+ cond = cond.And(builder.Eq{"ref": opts.Ref})
}
if 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})
+ cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
}
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 "`action_run`.`id` DESC"
+ return "`id` DESC"
}
type StatusInfo struct {
@@ -124,14 +112,14 @@ type StatusInfo struct {
}
// GetStatusInfoList returns a slice of StatusInfo
-func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
+func GetStatusInfoList(ctx context.Context) []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.LocaleString(lang),
+ DisplayedStatus: s.String(),
})
}
return statusInfoList
diff --git a/models/actions/runner.go b/models/actions/runner.go
index 81d4249ae0..c4e5f9d121 100644
--- a/models/actions/runner.go
+++ b/models/actions/runner.go
@@ -5,7 +5,6 @@ package actions
import (
"context"
- "errors"
"fmt"
"strings"
"time"
@@ -15,7 +14,6 @@ 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"
@@ -59,8 +57,6 @@ 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"`
@@ -88,10 +84,9 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
return types.OwnerTypeRepository
}
if r.OwnerID != 0 {
- switch r.Owner.Type {
- case user_model.UserTypeOrganization:
+ if r.Owner.Type == user_model.UserTypeOrganization {
return types.OwnerTypeOrganization
- case user_model.UserTypeIndividual:
+ } else if r.Owner.Type == user_model.UserTypeIndividual {
return types.OwnerTypeIndividual
}
}
@@ -125,15 +120,8 @@ func (r *ActionRunner) IsOnline() bool {
return false
}
-// 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")
- }
+// Editable checks if the runner is editable by the user
+func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
if ownerID == 0 && repoID == 0 {
return true
}
@@ -177,12 +165,6 @@ 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
@@ -279,7 +261,7 @@ func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) {
// UpdateRunner updates runner's information.
func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
e := db.GetEngine(ctx)
- r.Name = util.EllipsisDisplayString(r.Name, 255)
+ r.Name, _ = util.SplitStringAtByteN(r.Name, 255)
var err error
if len(cols) == 0 {
_, err = e.ID(r.ID).AllCols().Update(r)
@@ -299,23 +281,6 @@ 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 {
@@ -323,7 +288,7 @@ func CreateRunner(ctx context.Context, t *ActionRunner) error {
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
t.OwnerID = 0
}
- t.Name = util.EllipsisDisplayString(t.Name, 255)
+ t.Name, _ = util.SplitStringAtByteN(t.Name, 255)
return db.Insert(ctx, t)
}
@@ -372,17 +337,3 @@ 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 bbd2af73b6..1eab5efcce 100644
--- a/models/actions/runner_token.go
+++ b/models/actions/runner_token.go
@@ -10,6 +10,7 @@ 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/base"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
)
@@ -51,7 +52,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 "%s...": %w`, util.TruncateRunes(token, 3), util.ErrNotExist)
+ return nil, fmt.Errorf(`runner token "%s...": %w`, base.TruncateString(token, 3), util.ErrNotExist)
}
return &runnerToken, nil
}
diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go
index 21614b7086..159805e5f7 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.Equal(t, expectedToken, token)
+ assert.EqualValues(t, expectedToken, token)
}
func TestNewRunnerToken(t *testing.T) {
@@ -26,7 +26,7 @@ func TestNewRunnerToken(t *testing.T) {
assert.NoError(t, err)
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.Equal(t, expectedToken, token)
+ assert.EqualValues(t, expectedToken, token)
}
func TestUpdateRunnerToken(t *testing.T) {
@@ -36,5 +36,5 @@ func TestUpdateRunnerToken(t *testing.T) {
assert.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.Equal(t, expectedToken, token)
+ assert.EqualValues(t, expectedToken, token)
}
diff --git a/models/actions/schedule.go b/models/actions/schedule.go
index 2edf483fe0..961ffd0851 100644
--- a/models/actions/schedule.go
+++ b/models/actions/schedule.go
@@ -43,12 +43,15 @@ 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
@@ -65,7 +68,7 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
// Loop through each schedule row
for _, row := range rows {
- row.Title = util.EllipsisDisplayString(row.Title, 255)
+ row.Title, _ = util.SplitStringAtByteN(row.Title, 255)
// Create new schedule row
if err = db.Insert(ctx, row); err != nil {
return err
@@ -117,22 +120,21 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
return committer.Commit()
}
-func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
+func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) 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 nil, fmt.Errorf("DeleteCronTaskByRepo: %v", err)
+ return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- jobs, err := CancelPreviousJobs(
+ if err := CancelPreviousJobs(
ctx,
repo.ID,
repo.DefaultBranch,
"",
webhook_module.HookEventSchedule,
- )
- if err != nil {
- return jobs, fmt.Errorf("CancelPreviousJobs: %v", err)
+ ); err != nil {
+ return fmt.Errorf("CancelPreviousJobs: %v", err)
}
- return jobs, nil
+ return nil
}
diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go
index e26b2c1120..f7dac72f8b 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 := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
+ repos, err := GetReposMapByIDs(ctx, repoIDs)
if err != nil {
return err
}
diff --git a/models/actions/status.go b/models/actions/status.go
index 2b1d70613c..eda2234137 100644
--- a/models/actions/status.go
+++ b/models/actions/status.go
@@ -4,8 +4,6 @@
package actions
import (
- "slices"
-
"code.gitea.io/gitea/modules/translation"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
@@ -90,7 +88,12 @@ func (s Status) IsBlocked() bool {
// In returns whether s is one of the given statuses
func (s Status) In(statuses ...Status) bool {
- return slices.Contains(statuses, s)
+ for _, v := range statuses {
+ if s == v {
+ return true
+ }
+ }
+ return false
}
func (s Status) AsResult() runnerv1.Result {
diff --git a/models/actions/task.go b/models/actions/task.go
index e0756b10c2..af74faf937 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -6,7 +6,6 @@ package actions
import (
"context"
"crypto/subtle"
- "errors"
"fmt"
"time"
@@ -278,13 +277,14 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
return nil, false, err
}
- parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
- if err != nil {
+ var workflowJob *jobparser.Job
+ if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil {
return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err)
- } else if len(parsedWorkflows) != 1 {
+ } else if len(gots) != 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.EllipsisDisplayString(v.String(), 255)
+ name, _ := util.SplitStringAtByteN(v.String(), 255)
steps[i] = &ActionTaskStep{
Name: name,
TaskID: task.ID,
@@ -335,11 +335,6 @@ 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
}
@@ -366,7 +361,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, errors.New("invalid runner for task")
+ return nil, fmt.Errorf("invalid runner for task")
}
if task.Status.IsDone() {
diff --git a/models/actions/task_list.go b/models/actions/task_list.go
index 0c80397899..df4b43c5ef 100644
--- a/models/actions/task_list.go
+++ b/models/actions/task_list.go
@@ -48,7 +48,6 @@ func (tasks TaskList) LoadAttributes(ctx context.Context) error {
type FindTaskOptions struct {
db.ListOptions
RepoID int64
- JobID int64
OwnerID int64
CommitSHA string
Status Status
@@ -62,9 +61,6 @@ 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 f6ba661ae3..12657942fc 100644
--- a/models/actions/utils.go
+++ b/models/actions/utils.go
@@ -82,22 +82,3 @@ 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 7154843c17..163bb12c93 100644
--- a/models/actions/variable.go
+++ b/models/actions/variable.go
@@ -6,12 +6,10 @@ 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"
)
@@ -34,39 +32,26 @@ 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, description string) (*ActionVariable, error) {
+func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data 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,
- Description: description,
+ OwnerID: ownerID,
+ RepoID: repoID,
+ Name: strings.ToUpper(name),
+ Data: data,
}
return variable, db.Insert(ctx, variable)
}
@@ -111,12 +96,6 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
}
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).
@@ -168,17 +147,3 @@ 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 1a0dfe6412..4c71810c61 100644
--- a/models/activities/action.go
+++ b/models/activities/action.go
@@ -9,7 +9,6 @@ import (
"fmt"
"net/url"
"path"
- "slices"
"strconv"
"strings"
"time"
@@ -17,14 +16,16 @@ 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"
@@ -126,7 +127,12 @@ func (at ActionType) String() string {
}
func (at ActionType) InActions(actions ...string) bool {
- return slices.Contains(actions, at.String())
+ for _, action := range actions {
+ if action == at.String() {
+ return true
+ }
+ }
+ return false
}
// Action represents user operation type and other information to
@@ -168,10 +174,7 @@ func (a *Action) TableIndices() []*schemas.Index {
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}
+ indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
return indices
}
@@ -187,7 +190,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
return
}
var err error
- a.ActUser, err = user_model.GetPossibleUserByID(ctx, a.ActUserID)
+ a.ActUser, err = user_model.GetUserByID(ctx, a.ActUserID)
if err == nil {
return
} else if user_model.IsErrUserNotExist(err) {
@@ -197,13 +200,15 @@ func (a *Action) LoadActUser(ctx context.Context) {
}
}
-func (a *Action) LoadRepo(ctx context.Context) error {
+func (a *Action) LoadRepo(ctx context.Context) {
if a.Repo != nil {
- return nil
+ return
}
var err error
a.Repo, err = repo_model.GetRepositoryByID(ctx, a.RepoID)
- return err
+ if err != nil {
+ log.Error("repo_model.GetRepositoryByID(%d): %v", a.RepoID, err)
+ }
}
// GetActFullName gets the action's user full name.
@@ -221,7 +226,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 util.EllipsisDisplayString(a.GetActUserName(ctx), 20)
+ return base.EllipsisString(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.
@@ -245,7 +250,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)"
}
@@ -255,12 +260,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 util.EllipsisDisplayString(a.GetRepoUserName(ctx), 20)
+ return base.EllipsisString(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)"
}
@@ -270,7 +275,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 util.EllipsisDisplayString(a.GetRepoName(ctx), 33)
+ return base.EllipsisString(a.GetRepoName(ctx), 33)
}
// GetRepoPath returns the virtual path to the action repository.
@@ -350,7 +355,7 @@ func (a *Action) GetBranch() string {
// GetRefLink returns the action's ref link.
func (a *Action) GetRefLink(ctx context.Context) string {
- return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
+ return git.RefURL(a.GetRepoLink(ctx), a.RefName)
}
// GetTag returns the action's repository tag.
@@ -441,7 +446,6 @@ 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
@@ -450,24 +454,6 @@ 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()
@@ -525,8 +511,8 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
}
if opts.RequestedTeam != nil {
- env := repo_model.AccessibleTeamReposEnv(organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
- teamRepoIDs, err := env.RepoIDs(ctx)
+ env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(ctx, opts.RequestedTeam)
+ teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
if err != nil {
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
}
@@ -548,7 +534,17 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
cond = cond.And(builder.Eq{"is_deleted": false})
}
- cond = cond.And(FeedDateCond(opts))
+ 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()})
+ }
+ }
return cond, nil
}
@@ -563,6 +559,130 @@ 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 b52cf7ee49..5f9acb8f2a 100644
--- a/models/activities/action_list.go
+++ b/models/activities/action_list.go
@@ -5,7 +5,6 @@ package activities
import (
"context"
- "errors"
"fmt"
"strconv"
@@ -206,34 +205,12 @@ 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, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
+ return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
- var err error
- var cond builder.Cond
- // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
- if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
- cond = builder.Eq{
- "user_id": opts.RequestedUser.ID,
- }.And(
- FeedDateCond(opts),
- )
-
- if !opts.IncludeDeleted {
- cond = cond.And(builder.Eq{"is_deleted": false})
- }
-
- if !opts.IncludePrivate {
- cond = cond.And(builder.Eq{"is_private": false})
- }
- if opts.OnlyPerformedBy {
- cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
- }
- } else {
- cond, err = ActivityQueryCondition(ctx, opts)
- if err != nil {
- return nil, 0, err
- }
+ cond, err := ActivityQueryCondition(ctx, opts)
+ if err != nil {
+ return nil, 0, err
}
actions := make([]*Action, 0, opts.PageSize)
@@ -244,11 +221,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
sess := db.GetEngine(ctx).Where(cond)
sess = db.SetSessionPagination(sess, &opts)
- if opts.DontCount {
- err = sess.Desc("`action`.created_unix").Find(&actions)
- } else {
- count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
- }
+ count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
if err != nil {
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
@@ -262,13 +235,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, fmt.Errorf("Find(actionsIDs): %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)
- }
+ 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 {
@@ -282,9 +253,3 @@ 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 ff311ac891..9cfe981656 100644
--- a/models/activities/action_test.go
+++ b/models/activities/action_test.go
@@ -82,6 +82,43 @@ 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.")
@@ -130,7 +167,7 @@ func TestDeleteIssueActions(t *testing.T) {
// load an issue
issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4})
- assert.NotEqual(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
+ assert.NotEqualValues(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 b47f5dc404..0cbb91df3c 100644
--- a/models/activities/notification_list.go
+++ b/models/activities/notification_list.go
@@ -208,7 +208,10 @@ 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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Rows(new(repo_model.Repository))
@@ -279,7 +282,10 @@ 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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(issues_model.Issue))
@@ -371,7 +377,10 @@ 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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", userIDs[:limit]).
Rows(new(user_model.User))
@@ -419,7 +428,10 @@ 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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
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 5d2a29bc36..52f0eacba1 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.Equal(t, user.ID, notfs[0].UserID)
+ assert.EqualValues(t, user.ID, notfs[0].UserID)
assert.EqualValues(t, 4, notfs[1].ID)
- assert.Equal(t, user.ID, notfs[1].UserID)
+ assert.EqualValues(t, user.ID, notfs[1].UserID)
assert.EqualValues(t, 2, notfs[2].ID)
- assert.Equal(t, user.ID, notfs[2].UserID)
+ assert.EqualValues(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.Equal(t, notf.RepoID, repo.ID)
+ assert.EqualValues(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.Equal(t, notf.IssueID, issue.ID)
+ assert.EqualValues(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.Equal(t, activities_model.NotificationStatusRead, nt.Status)
+ assert.EqualValues(t, activities_model.NotificationStatusRead, nt.Status)
}
diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go
index aeaa452c9e..3ccdbd47d3 100644
--- a/models/activities/repo_activity.go
+++ b/models/activities/repo_activity.go
@@ -139,7 +139,10 @@ func GetActivityStatsTopAuthors(ctx context.Context, repo *repo_model.Repository
return v[i].Commits > v[j].Commits
})
- cnt := min(count, len(v))
+ cnt := count
+ if cnt > len(v) {
+ cnt = len(v)
+ }
return v[:cnt], nil
}
diff --git a/models/activities/statistic.go b/models/activities/statistic.go
index 940651d359..ff81ad78a1 100644
--- a/models/activities/statistic.go
+++ b/models/activities/statistic.go
@@ -17,16 +17,13 @@ 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 {
- UsersActive, UsersNotActive,
- Org, PublicKey,
+ User, Org, PublicKey,
Repo, Watch, Star, Access,
Issue, IssueClosed, IssueOpen,
Comment, Oauth, Follow,
@@ -56,20 +53,8 @@ type IssueByRepositoryCount struct {
// GetStatistic returns the database statistics
func GetStatistic(ctx context.Context) (stats Statistic) {
e := db.GetEngine(ctx)
-
- // 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.User = user_model.CountUsers(ctx, nil)
+ stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true})
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 ef67838be7..1f8f0f590e 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()-(366+7)*86400). // (366+7) days to include the first week for the heatmap
+ And("created_unix > ?", timeutil.TimeStampNow()-31536000).
GroupBy(groupByName).
OrderBy("timestamp").
Find(&hdata)
diff --git a/models/activities/user_heatmap_test.go b/models/activities/user_heatmap_test.go
index 380045d3c5..a039fd3613 100644
--- a/models/activities/user_heatmap_test.go
+++ b/models/activities/user_heatmap_test.go
@@ -64,9 +64,11 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
for _, tc := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
- var doer *user_model.User
- if tc.doerID != 0 {
- doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
+ doer := &user_model.User{ID: tc.doerID}
+ _, err := unittest.LoadBeanIfExists(doer)
+ assert.NoError(t, err)
+ if tc.doerID == 0 {
+ doer = nil
}
// get the action for comparison
diff --git a/models/admin/task.go b/models/admin/task.go
index 0541a8ec78..10f8e6d570 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 b765624579..03bc82302f 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.ErrorWrap(util.ErrInvalidArgument, "the provided key is a private key")
+var ErrKeyIsPrivate = util.NewSilentWrapErrorf(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 "failed to parse gpg key " + err.ParseError.Error()
+ return fmt.Sprintf("failed to parse gpg key %s", err.ParseError.Error())
}
// ErrGPGKeyNotExist represents a "GPGKeyNotExist" kind of error.
@@ -217,7 +217,6 @@ func (err ErrGPGKeyAccessDenied) Unwrap() error {
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
type ErrKeyAccessDenied struct {
UserID int64
- RepoID int64
KeyID int64
Note string
}
@@ -229,8 +228,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, repo_id: %d, key_id: %d, note: %s]",
- err.UserID, err.RepoID, err.KeyID, err.Note)
+ 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)
}
func (err ErrKeyAccessDenied) Unwrap() error {
diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go
index 220f46ad1d..e921340730 100644
--- a/models/asymkey/gpg_key.go
+++ b/models/asymkey/gpg_key.go
@@ -5,7 +5,6 @@ package asymkey
import (
"context"
- "errors"
"fmt"
"strings"
"time"
@@ -107,7 +106,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
if err != nil {
return nil, err
}
- keys, err := CheckArmoredGPGKeyString(impKey.Content)
+ keys, err := checkArmoredGPGKeyString(impKey.Content)
if err != nil {
return nil, err
}
@@ -116,7 +115,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
// parseSubGPGKey parse a sub Key
func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
- content, err := Base64EncPubKey(pubkey)
+ content, err := base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -184,7 +183,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
}
}
- content, err := Base64EncPubKey(pubkey)
+ content, err := base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -208,7 +207,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, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
+ return 0, fmt.Errorf("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))
@@ -241,9 +240,32 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
return committer.Commit()
}
-func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
- return db.Find[GPGKey](ctx, FindGPGKeyOptions{
- KeyID: keyID,
- IncludeSubKeys: true,
- })
+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
}
diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go
index 1c7d2c1da2..6c0f6e01a7 100644
--- a/models/asymkey/gpg_key_add.go
+++ b/models/asymkey/gpg_key_add.go
@@ -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
}
@@ -91,7 +91,7 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
}
if err != nil {
- log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err)
+ log.Error("Unable to validate token signature. Error: %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 39ec893606..9219a509df 100644
--- a/models/asymkey/gpg_key_commit_verification.go
+++ b/models/asymkey/gpg_key_commit_verification.go
@@ -4,13 +4,17 @@
package asymkey
import (
- "errors"
+ "context"
"fmt"
"hash"
+ "strings"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"github.com/ProtonMail/go-crypto/openpgp/packet"
)
@@ -66,10 +70,267 @@ 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 errors.New("key can not sign")
+ return fmt.Errorf("key can not sign")
}
// Decode key
pkey, err := base64DecPubKey(k.Content)
@@ -108,7 +369,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{
@@ -131,6 +392,78 @@ 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 76f52a3ca4..92c34a2569 100644
--- a/models/asymkey/gpg_key_common.go
+++ b/models/asymkey/gpg_key_common.go
@@ -7,7 +7,6 @@ import (
"bytes"
"crypto"
"encoding/base64"
- "errors"
"fmt"
"hash"
"io"
@@ -34,9 +33,9 @@ import (
// This file provides common functions relating to GPG Keys
-// CheckArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
+// checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
// The function returns the actual public key on success
-func CheckArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
+func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content))
if err != nil {
return nil, ErrGPGKeyParsing{err}
@@ -44,8 +43,8 @@ func CheckArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
return list, nil
}
-// Base64EncPubKey encode public key content to base 64
-func Base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
+// base64EncPubKey encode public key content to base 64
+func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
var w bytes.Buffer
err := pubkey.Serialize(&w)
if err != nil {
@@ -76,7 +75,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
// Check type
pkey, ok := p.(*packet.PublicKey)
if !ok {
- return nil, errors.New("key is not a public key")
+ return nil, fmt.Errorf("key is not a public key")
}
return pkey, nil
}
@@ -120,23 +119,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, errors.New("Failed to read signature armor")
+ return nil, fmt.Errorf("Failed to read signature armor")
}
p, err := packet.Read(r)
if err != nil {
- return nil, errors.New("Failed to read signature packet")
+ return nil, fmt.Errorf("Failed to read signature packet")
}
sig, ok := p.(*packet.Signature)
if !ok {
- return nil, errors.New("Packet is not a signature")
+ return nil, fmt.Errorf("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 408cf15763..de463dfbe2 100644
--- a/models/asymkey/gpg_key_test.go
+++ b/models/asymkey/gpg_key_test.go
@@ -16,7 +16,6 @@ import (
"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) {
@@ -51,7 +50,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
}
@@ -72,7 +71,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
}
@@ -108,14 +107,15 @@ Av844q/BfRuVsJsK1NDNG09LC30B0l3LKBqlrRmRTUMHtgchdX2dY+p7GPOoSzlR
MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
=i9b7
-----END PGP PUBLIC KEY BLOCK-----`
- keys, err := CheckArmoredGPGKeyString(testGPGArmor)
- require.NotEmpty(t, keys)
-
+ keys, err := checkArmoredGPGKeyString(testGPGArmor)
+ if !assert.NotEmpty(t, keys) {
+ return
+ }
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{
@@ -176,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
@@ -386,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]
@@ -396,11 +396,11 @@ 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},
}))
}
@@ -411,9 +411,9 @@ func TestParseGPGKey(t *testing.T) {
// create a key for test email
e, err := openpgp.NewEntity("name", "comment", "email1@example.com", nil)
- require.NoError(t, err)
+ assert.NoError(t, err)
k, err := parseGPGKey(db.DefaultContext, 1, e, true)
- require.NoError(t, err)
+ assert.NoError(t, err)
assert.NotEmpty(t, k.KeyID)
assert.NotEmpty(t, k.Emails) // the key is valid, matches the email
@@ -422,7 +422,7 @@ func TestParseGPGKey(t *testing.T) {
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.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 5ab2fd8081..01812a2d54 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.Debug("VerifyGPGKey failed: no signer")
+ log.Error("Unable to validate token signature. Error: %v", err)
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
}
diff --git a/models/asymkey/ssh_key_commit_verification.go b/models/asymkey/ssh_key_commit_verification.go
new file mode 100644
index 0000000000..27c6df3578
--- /dev/null
+++ b/models/asymkey/ssh_key_commit_verification.go
@@ -0,0 +1,80 @@
+// 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 4dcfe1f279..1ed3b5df2a 100644
--- a/models/asymkey/ssh_key_fingerprint.go
+++ b/models/asymkey/ssh_key_fingerprint.go
@@ -6,13 +6,27 @@ 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,
@@ -27,6 +41,29 @@ 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))
@@ -38,12 +75,15 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint(publicKeyContent string) (string, error) {
- fp, err := calcFingerprintNative(publicKeyContent)
+ // Call the method based on configuration
+ useNative := setting.SSH.KeygenPath == ""
+ calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
+ fp, err := calcFn(publicKeyContent)
if err != nil {
if IsErrKeyUnableVerify(err) {
return "", err
}
- return "", fmt.Errorf("CalcFingerprint: %w", err)
+ return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
}
return fp, nil
}
diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go
index fc39f28624..94b1cf112b 100644
--- a/models/asymkey/ssh_key_parse.go
+++ b/models/asymkey/ssh_key_parse.go
@@ -10,12 +10,14 @@ 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"
@@ -91,7 +93,7 @@ func parseKeyString(content string) (string, error) {
block, _ := pem.Decode([]byte(content))
if block == nil {
- return "", errors.New("failed to parse PEM block containing the public key")
+ return "", fmt.Errorf("failed to parse PEM block containing the public key")
}
if strings.Contains(block.Type, "PRIVATE") {
return "", ErrKeyIsPrivate
@@ -172,9 +174,20 @@ func CheckPublicKeyString(content string) (_ string, err error) {
return content, nil
}
- keyType, length, err := SSHNativeParsePublicKey(content)
+ 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)
+ }
if err != nil {
- return "", fmt.Errorf("SSHNativeParsePublicKey: %w", err)
+ return "", fmt.Errorf("%s: %w", fnName, err)
}
log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
@@ -208,7 +221,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: //nolint:staticcheck // it's deprecated
+ case ssh.KeyAlgoDSA:
rawPub := struct {
Name string
P, Q, G, Y *big.Int
@@ -244,3 +257,56 @@ 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 21e4ddf62e..3650f1892f 100644
--- a/models/asymkey/ssh_key_test.go
+++ b/models/asymkey/ssh_key_test.go
@@ -42,7 +42,29 @@ func Test_SSHParsePublicKey(t *testing.T) {
keyTypeN, lengthN, err := SSHNativeParsePublicKey(tc.content)
assert.NoError(t, err)
assert.Equal(t, tc.keyType, keyTypeN)
- assert.Equal(t, tc.length, lengthN)
+ 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)
})
})
}
@@ -164,6 +186,14 @@ 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 0cf29ca9f1..208288c77b 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(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
+ err = sshsig.Verify(bytes.NewBuffer([]byte(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(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
- log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
+ 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)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
}
diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go
index 3eae19b2a5..897ff3fc9e 100644
--- a/models/auth/access_token_scope.go
+++ b/models/auth/access_token_scope.go
@@ -5,7 +5,6 @@ package auth
import (
"fmt"
- "slices"
"strings"
"code.gitea.io/gitea/models/perm"
@@ -15,7 +14,7 @@ import (
type AccessTokenScopeCategory int
const (
- AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota
+ AccessTokenScopeCategoryActivityPub = iota
AccessTokenScopeCategoryAdmin
AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values
AccessTokenScopeCategoryNotification
@@ -194,14 +193,6 @@ 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))
@@ -213,7 +204,12 @@ func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTok
// ContainsCategory checks if a list of categories contains a specific category
func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool {
- return slices.Contains(categories, category)
+ for _, c := range categories {
+ if c == category {
+ return true
+ }
+ }
+ return false
}
// GetScopeLevelFromAccessMode converts permission access mode to scope level
@@ -274,9 +270,6 @@ 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), ",")
}
@@ -290,10 +283,6 @@ 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 b93c25528f..a6097e45d7 100644
--- a/models/auth/access_token_scope_test.go
+++ b/models/auth/access_token_scope_test.go
@@ -17,7 +17,6 @@ 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},
@@ -26,13 +25,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 GetAccessTokenCategories() {
+ for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
tests = append(tests,
- 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},
+ 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},
)
}
@@ -60,23 +59,23 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
{"public-only", "read:issue", false, nil},
}
- for _, scope := range GetAccessTokenCategories() {
+ for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
tests = append(tests,
scopeTestHasScope{
- AccessTokenScope("read:" + scope),
- AccessTokenScope("read:" + scope), true, nil,
+ AccessTokenScope(fmt.Sprintf("read:%s", scope)),
+ AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
},
scopeTestHasScope{
- AccessTokenScope("write:" + scope),
- AccessTokenScope("write:" + scope), true, nil,
+ AccessTokenScope(fmt.Sprintf("write:%s", scope)),
+ AccessTokenScope(fmt.Sprintf("write:%s", scope)), true, nil,
},
scopeTestHasScope{
- AccessTokenScope("write:" + scope),
- AccessTokenScope("read:" + scope), true, nil,
+ AccessTokenScope(fmt.Sprintf("write:%s", scope)),
+ AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
},
scopeTestHasScope{
- AccessTokenScope("read:" + scope),
- AccessTokenScope("write:" + scope), false, nil,
+ AccessTokenScope(fmt.Sprintf("read:%s", scope)),
+ AccessTokenScope(fmt.Sprintf("write:%s", scope)), false, nil,
},
)
}
diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go
index 54ff5a0d75..81f07d1a83 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 // export stutter
+type AuthToken struct { //nolint:revive
ID string `xorm:"pk"`
TokenHash string
UserID int64 `xorm:"INDEX"`
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index c2b6690116..c270e4856e 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -12,7 +12,6 @@ import (
"fmt"
"net"
"net/url"
- "slices"
"strings"
"code.gitea.io/gitea/models/db"
@@ -512,7 +511,12 @@ 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 {
- return slices.Contains(strings.Split(grant.Scope, " "), scope)
+ for _, currentScope := range strings.Split(grant.Scope, " ") {
+ if scope == currentScope {
+ return true
+ }
+ }
+ return false
}
// SetNonce updates the current nonce value of a grant
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index c6626b283e..43daa0b5ec 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 b.Loop() {
+ for i := 0; i < b.N; i++ {
_, _ = 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.Empty(t, grant.Scope)
+ assert.Equal(t, "", grant.Scope)
}
//////////////////// Grant
diff --git a/models/auth/source.go b/models/auth/source.go
index 7d7bc0f03c..a3a250cd91 100644
--- a/models/auth/source.go
+++ b/models/auth/source.go
@@ -58,15 +58,6 @@ 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
@@ -113,15 +104,19 @@ 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"`
- TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
- Cfg Config `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"`
+ Cfg convert.Conversion `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -145,7 +140,9 @@ func (source *Source) BeforeSet(colName string, val xorm.Cell) {
return
}
source.Cfg = constructor()
- source.Cfg.SetAuthSource(source)
+ if settable, ok := source.Cfg.(SourceSettable); ok {
+ settable.SetAuthSource(source)
+ }
}
}
@@ -203,10 +200,6 @@ 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 {
@@ -230,7 +223,9 @@ func CreateSource(ctx context.Context, source *Source) error {
return nil
}
- source.Cfg.SetAuthSource(source)
+ if settable, ok := source.Cfg.(SourceSettable); ok {
+ settable.SetAuthSource(source)
+ }
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
@@ -325,7 +320,9 @@ func UpdateSource(ctx context.Context, source *Source) error {
return nil
}
- source.Cfg.SetAuthSource(source)
+ if settable, ok := source.Cfg.(SourceSettable); ok {
+ settable.SetAuthSource(source)
+ }
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 64c7460b64..36e76d5e28 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -13,14 +13,10 @@ 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
@@ -58,8 +54,7 @@ func TestDumpAuthSource(t *testing.T) {
sb := new(strings.Builder)
- // 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))
+ db.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 200ce7c7c0..d0c341a192 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -164,13 +164,3 @@ 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 654427e974..f1cf398adf 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.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
+ unittest.AssertExistsIf(t, true, &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.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
+ unittest.AssertExistsIf(t, true, &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.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
+ unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
}
diff --git a/models/db/collation.go b/models/db/collation.go
index 79ade87380..a7db9f5442 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(xormEngine)
+ return CheckCollations(x)
}
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
diff --git a/models/db/context.go b/models/db/context.go
index ad99ada8c8..171e26b933 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 := range callerNum {
+ for i := 0; i < callerNum; i++ {
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 := range callerNum {
+ for i := 0; i < callerNum; i++ {
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
panic(errors.New("using database context in an iterator would cause corrupted results"))
}
@@ -94,7 +94,7 @@ func GetEngine(ctx context.Context) Engine {
if e := getExistingEngine(ctx); e != nil {
return e
}
- return xormEngine.Context(ctx)
+ return x.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 := xormEngine.NewSession()
+ sess := x.NewSession()
if err := sess.Begin(); err != nil {
_ = sess.Close()
return nil, nil, err
@@ -178,17 +178,8 @@ 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 := xormEngine.NewSession()
+ sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
@@ -298,9 +289,6 @@ 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
}
@@ -334,7 +322,7 @@ func CountByBean(ctx context.Context, bean any) (int64, error) {
// TableName returns the table name according a bean object
func TableName(bean any) string {
- return xormEngine.TableName(bean)
+ return x.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 a6bd11d2ae..e8c6b74d93 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.Equal(t, testCount, actualCount)
+ assert.EqualValues(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 80b0f7b04b..8c124471ab 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 xormEngine.Dialect().URI().DBType != schemas.MYSQL {
+ if x.Dialect().URI().DBType != schemas.MYSQL {
return nil
}
- r, err := CheckCollations(xormEngine)
+ r, err := CheckCollations(x)
if err != nil {
return err
}
- _, err = xormEngine.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
+ _, err = x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
if err != nil {
return err
}
- tables, err := xormEngine.DBMetas()
+ tables, err := x.DBMetas()
if err != nil {
return err
}
for _, table := range tables {
- if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
+ if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
return err
}
- if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
+ if _, err := x.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 xormEngine.Dialect().URI().DBType != schemas.MSSQL {
+ if x.Dialect().URI().DBType != schemas.MSSQL {
return nil
}
- sess := xormEngine.NewSession()
+ sess := x.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 ba287d58f0..b17188945a 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -8,10 +8,17 @@ 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
@@ -20,9 +27,9 @@ import (
)
var (
- xormEngine *xorm.Engine
- registeredModels []any
- registeredInitFuncs []func() error
+ x *xorm.Engine
+ tables []any
+ initFuncs []func() error
)
// Engine represents a xorm engine or session.
@@ -63,38 +70,167 @@ type Engine interface {
// TableInfo returns table's information via an object
func TableInfo(v any) (*schemas.Table, error) {
- return xormEngine.TableInfo(v)
+ return x.TableInfo(v)
}
-// RegisterModel registers model, if initFuncs provided, it will be invoked after data model sync
+// 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
func RegisterModel(bean any, initFunc ...func() error) {
- registeredModels = append(registeredModels, bean)
- if len(registeredInitFuncs) > 0 && initFunc[0] != nil {
- registeredInitFuncs = append(registeredInitFuncs, initFunc[0])
+ tables = append(tables, bean)
+ if len(initFuncs) > 0 && initFunc[0] != nil {
+ initFuncs = append(initFuncs, 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 := xormEngine.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
+ _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
WarnIfDatabaseColumnMissed: true,
- }, registeredModels...)
+ }, tables...)
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, registeredModels...)
+ beans = append(beans, tables...)
return beans, nil
}
// Need to map provided names to beans...
beanMap := make(map[string]any)
- for _, bean := range registeredModels {
+ for _, bean := range tables {
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
- beanMap[strings.ToLower(xormEngine.TableName(bean))] = bean
- beanMap[strings.ToLower(xormEngine.TableName(bean, true))] = bean
+ beanMap[strings.ToLower(x.TableName(bean))] = bean
+ beanMap[strings.ToLower(x.TableName(bean, true))] = bean
}
gotBean := make(map[any]bool)
@@ -111,9 +247,36 @@ 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 := xormEngine.TableInfo(bean)
+ t, err := x.TableInfo(bean)
if err != nil {
return 50
}
@@ -122,18 +285,18 @@ func MaxBatchInsertSize(bean any) int {
// IsTableNotEmpty returns true if table has at least one record
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
- return xormEngine.Table(beanOrTableName).Exist()
+ return x.Table(beanOrTableName).Exist()
}
// DeleteAllRecords will delete all the records of this table
func DeleteAllRecords(tableName string) error {
- _, err := xormEngine.Exec("DELETE FROM " + tableName)
+ _, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
return err
}
// GetMaxID will return max id of the table
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
- _, err = xormEngine.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
+ _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
return maxID, err
}
@@ -145,3 +308,24 @@ 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
deleted file mode 100644
index 63f2d4e093..0000000000
--- a/models/db/engine_dump.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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
deleted file mode 100644
index 8709a2c2a1..0000000000
--- a/models/db/engine_hook.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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
deleted file mode 100644
index bb02aff274..0000000000
--- a/models/db/engine_init.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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 a236f83735..e3dbfbdc24 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -15,7 +15,6 @@ import (
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestDumpDatabase(t *testing.T) {
@@ -52,7 +51,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.Equal(t, countBefore, countAfter)
+ assert.EqualValues(t, countBefore, countAfter)
}
func TestPrimaryKeys(t *testing.T) {
@@ -63,7 +62,9 @@ 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()
- require.NoError(t, err)
+ if err != nil {
+ t.Fatal(err)
+ }
whitelist := map[string]string{
"the_table_name_to_skip_checking": "Write a note here to explain why",
@@ -78,6 +79,8 @@ func TestPrimaryKeys(t *testing.T) {
t.Logf("ignore %q because %q", table.Name, why)
continue
}
- assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
+ if len(table.PrimaryKeys) == 0 {
+ t.Errorf("table %q has no primary key", table.Name)
+ }
}
}
diff --git a/models/db/error.go b/models/db/error.go
index d47c7adac4..665e970e17 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 name + " does not exist"
+ return fmt.Sprintf("%s does not exist", name)
}
// Unwrap unwraps this as a ErrNotExist err
diff --git a/models/db/list_test.go b/models/db/list_test.go
index 170473a968..45194611f8 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.Equal(t, cnt, newCnt)
+ assert.EqualValues(t, cnt, newCnt)
assert.Len(t, repoUnits, repoUnitCount)
}
diff --git a/models/db/log.go b/models/db/log.go
index a9df6f541d..307788ea2e 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, &log.Event{Level: level}, format, v...)
+ l.logger.Log(skip+1, level, format, v...)
}
// Debug show debug log
diff --git a/models/db/name.go b/models/db/name.go
index 48c7fdbce5..51be33a8bc 100644
--- a/models/db/name.go
+++ b/models/db/name.go
@@ -5,13 +5,21 @@ package db
import (
"fmt"
- "slices"
+ "regexp"
"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
@@ -74,18 +82,20 @@ 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 a prefix or suffix match with placeholder '*'.
-func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
+// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
+func IsUsableName(names, patterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
- return util.NewInvalidArgumentErrorf("name is empty")
+ return ErrNameEmpty
}
- if slices.Contains(reservedNames, name) {
- return ErrNameReserved{name}
+ for i := range names {
+ if name == names[i] {
+ return ErrNameReserved{name}
+ }
}
- for _, pat := range reservedPatterns {
+ for _, pat := range patterns {
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 44d54f21fc..e0a1b6bde9 100644
--- a/models/db/search.go
+++ b/models/db/search.go
@@ -29,3 +29,7 @@ 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 9adc5113ac..f49ad935de 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 := xormEngine.NewSession()
+ sess := x.NewSession()
defer sess.Close()
var sequences []string
- schema := xormEngine.Dialect().URI().Schema
+ schema := x.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 := xormEngine.NewSession()
+ sess := x.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 812fe4a6a6..64b61b2ef3 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 // see above
+ if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck
_, 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 // see above
+ _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck
if err != nil {
_ = conn.Close()
return nil, err
diff --git a/models/dbfs/dbfile.go b/models/dbfs/dbfile.go
index eaf506fbe6..dd27b5c36b 100644
--- a/models/dbfs/dbfile.go
+++ b/models/dbfs/dbfile.go
@@ -46,7 +46,10 @@ 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 := min(len(p), blobRemaining)
+ needRead := len(p)
+ if needRead > blobRemaining {
+ needRead = blobRemaining
+ }
if blobOffset+int64(blobPos)+int64(needRead) > fileMeta.FileSize {
needRead = int(fileMeta.FileSize - blobOffset - int64(blobPos))
}
@@ -63,8 +66,14 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
blobData = nil
}
- canCopy := max(len(blobData)-blobPos, 0)
- realRead := min(needRead, canCopy)
+ canCopy := len(blobData) - blobPos
+ if canCopy <= 0 {
+ canCopy = 0
+ }
+ realRead := needRead
+ if realRead > canCopy {
+ realRead = canCopy
+ }
if realRead > 0 {
copy(p[:realRead], fileData.BlobData[blobPos:blobPos+realRead])
}
@@ -104,7 +113,10 @@ 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 := min(len(p), blobRemaining)
+ needWrite := len(p)
+ if needWrite > blobRemaining {
+ needWrite = 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 0257d2bd15..96cb1014c7 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.Equal(t, 10, n)
+ assert.EqualValues(t, 10, n)
_, err = f.Seek(0, io.SeekStart)
assert.NoError(t, err)
buf, err := io.ReadAll(f)
assert.NoError(t, err)
- assert.Equal(t, 10, n)
- assert.Equal(t, "0123456789", string(buf))
+ assert.EqualValues(t, 10, n)
+ assert.EqualValues(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.Equal(t, "9", string(buf))
+ assert.EqualValues(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.Equal(t, "0bcdefghi9", string(buf))
+ assert.EqualValues(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.Equal(t, "0bcdefghiJKLMNOP", string(buf))
+ assert.EqualValues(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.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf))
+ assert.EqualValues(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.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf))
+ assert.EqualValues(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.Equal(t, "test.txt", stat.Name())
+ assert.EqualValues(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.Equal(t, "line 1\n", line)
+ assert.EqualValues(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.Equal(t, "line 2\n", line)
+ assert.EqualValues(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.Equal(t, "111333", string(buf))
+ assert.EqualValues(t, "111333", string(buf))
}
diff --git a/models/error.go b/models/error.go
new file mode 100644
index 0000000000..75c53245de
--- /dev/null
+++ b/models/error.go
@@ -0,0 +1,552 @@
+// 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 596046e950..4171e31fef 100644
--- a/models/fixtures/access.yml
+++ b/models/fixtures/access.yml
@@ -171,9 +171,3 @@
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 ee8ef0d5ce..2c51c11ebd 100644
--- a/models/fixtures/action_artifact.yml
+++ b/models/fixtures/action_artifact.yml
@@ -11,24 +11,6 @@
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
@@ -87,57 +69,3 @@
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 09dfa6cccb..1db849352f 100644
--- a/models/fixtures/action_run.yml
+++ b/models/fixtures/action_run.yml
@@ -9,7 +9,6 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
- trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -29,7 +28,6 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
- trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -49,9 +47,8 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
- trigger_event: "push"
is_fork_pull_request: 0
- status: 6 # running
+ status: 1
started: 1683636528
stopped: 1683636626
created: 1683636108
@@ -69,7 +66,6 @@
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
- trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -78,64 +74,3 @@
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 6c06d94aa4..8837e6ec2d 100644
--- a/models/fixtures/action_run_job.yml
+++ b/models/fixtures/action_run_job.yml
@@ -69,63 +69,3 @@
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
- stopped: 1683636626
diff --git a/models/fixtures/action_runner.yml b/models/fixtures/action_runner.yml
deleted file mode 100644
index ecb7214006..0000000000
--- a/models/fixtures/action_runner.yml
+++ /dev/null
@@ -1,51 +0,0 @@
--
- 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 c79fb07050..506a47d8a0 100644
--- a/models/fixtures/action_task.yml
+++ b/models/fixtures/action_task.yml
@@ -117,63 +117,3 @@
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 03e21d04b4..17b1869ab6 100644
--- a/models/fixtures/branch.yml
+++ b/models/fixtures/branch.yml
@@ -93,123 +93,3 @@
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 87c652e53a..20d57975ef 100644
--- a/models/fixtures/commit_status.yml
+++ b/models/fixtures/commit_status.yml
@@ -7,7 +7,6 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
- context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -19,7 +18,6 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
- context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -31,7 +29,6 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
- context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -43,7 +40,6 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
- context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -55,5 +51,4 @@
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 0f6bd9ee6d..b2a0432635 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 6023719b1e..d573406b36 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: true
+ is_delivered: false
-
id: 3
diff --git a/models/fixtures/issue_pin.yml b/models/fixtures/issue_pin.yml
deleted file mode 100644
index 14b7a72d84..0000000000
--- a/models/fixtures/issue_pin.yml
+++ /dev/null
@@ -1,6 +0,0 @@
--
- id: 1
- repo_id: 2
- issue_id: 4
- is_pull: false
- pin_order: 1
diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml
index b12e6b207f..db92c95248 100644
--- a/models/fixtures/repo_transfer.yml
+++ b/models/fixtures/repo_transfer.yml
@@ -21,11 +21,3 @@
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 552a78cbd2..bbb028eb7b 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -1694,6 +1694,19 @@
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 976a236011..b3bece5589 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: 14
+ num_repos: 15
num_teams: 0
num_members: 0
visibility: 0
diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml
index ec282914b8..f62bae1f31 100644
--- a/models/fixtures/webhook.yml
+++ b/models/fixtures/webhook.yml
@@ -1,7 +1,7 @@
-
id: 1
repo_id: 1
- url: https://www.example.com/url1
+ url: 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: https://www.example.com/url2
+ url: 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,35 +18,14 @@
id: 3
owner_id: 3
repo_id: 3
- url: https://www.example.com/url3
+ url: 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: https://www.example.com/url4
+ url: 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 07c94a8ba5..d1caa35947 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -173,18 +173,6 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
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))
@@ -235,11 +223,6 @@ 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))
@@ -487,7 +470,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})
diff --git a/models/git/branch_test.go b/models/git/branch_test.go
index 252dcc5690..b8ea663e81 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.Equal(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
+ assert.EqualValues(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 f85e1b15e5..0579a41209 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 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:"-"`
+ 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:"-"`
CreatorID int64
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
@@ -222,7 +222,7 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
}
}
- prefix := status.Repo.Link() + "/actions"
+ prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
@@ -230,25 +230,22 @@ 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 {
- if len(statuses) == 0 {
- return nil
- }
-
- states := make(commitstatus.CommitStatusStates, 0, len(statuses))
- targetURL := ""
+ var lastStatus *CommitStatus
+ state := api.CommitStatusSuccess
for _, status := range statuses {
- states = append(states, status.State)
- if status.TargetURL != "" {
- targetURL = status.TargetURL
+ if status.State.NoBetterThan(state) {
+ state = status.State
+ lastStatus = status
}
}
-
- return &CommitStatus{
- RepoID: statuses[0].RepoID,
- SHA: statuses[0].SHA,
- State: states.Combine(),
- TargetURL: targetURL,
+ if lastStatus == nil {
+ if len(statuses) > 0 {
+ lastStatus = statuses[0]
+ } else {
+ lastStatus = &CommitStatus{}
+ }
}
+ return lastStatus
}
// CommitStatusOptions holds the options for query commit statuses
@@ -301,37 +298,27 @@ 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, error) {
+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)
+ }
indices := make([]int64, 0, 10)
- sess := makeRepoCommitQuery(ctx, repoID, sha).
- Select("max( `index` ) as `index`").
- GroupBy("context_hash").
- OrderBy("max( `index` ) desc")
+ sess := getBase().Select("max( `index` ) as `index`").
+ GroupBy("context_hash").OrderBy("max( `index` ) desc")
if !listOptions.IsListAll() {
sess = db.SetSessionPagination(sess, &listOptions)
}
- if err := sess.Find(&indices); err != nil {
- return nil, err
+ count, err := sess.FindAndCount(&indices)
+ if err != nil {
+ return nil, count, err
}
statuses := make([]*CommitStatus, 0, len(indices))
if len(indices) == 0 {
- return statuses, nil
+ return statuses, count, nil
}
- 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()
+ return statuses, count, getBase().And(builder.In("`index`", indices)).Find(&statuses)
}
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
@@ -466,8 +453,9 @@ 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", opts.Repo.FullName(), opts.SHA)
+ return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
}
ctx, committer, err := db.TxContext(ctx)
@@ -489,13 +477,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", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
+ log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, 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", opts.Repo.FullName(), opts.SHA, err)
+ return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoPath, opts.SHA, err)
}
return committer.Commit()
@@ -508,11 +496,47 @@ 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 dd416fa015..7603e7aa65 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 commitstatus.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 api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+ TargetURL string `xorm:"TEXT"`
}
func init() {
@@ -55,15 +55,11 @@ 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
}
- // 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
+ state := CalcCommitStatus(commitStatuses)
// 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 4c0f5e891b..37d785e938 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" // the mocked commit ID in test fixtures
+ sha1 := "1234123412341234123412341234123412341234"
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, commitstatus.CommitStatusPending, statuses[0].State)
+ assert.Equal(t, structs.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, commitstatus.CommitStatusWarning, statuses[1].State)
+ assert.Equal(t, structs.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, commitstatus.CommitStatusSuccess, statuses[2].State)
+ assert.Equal(t, structs.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, commitstatus.CommitStatusFailure, statuses[3].State)
+ assert.Equal(t, structs.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, commitstatus.CommitStatusError, statuses[4].State)
+ assert.Equal(t, structs.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: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusError,
+ State: structs.CommitStatusError,
},
{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusFailure,
+ State: structs.CommitStatusError,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusWarning,
+ State: structs.CommitStatusWarning,
},
{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusPending,
},
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusPending,
+ State: structs.CommitStatusWarning,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: commitstatus.CommitStatusFailure,
+ State: structs.CommitStatusFailure,
},
{
- State: commitstatus.CommitStatusError,
+ State: structs.CommitStatusError,
},
{
- State: commitstatus.CommitStatusWarning,
+ State: structs.CommitStatusWarning,
},
},
expected: &git_model.CommitStatus{
- State: commitstatus.CommitStatusFailure,
+ State: structs.CommitStatusError,
},
},
}
for _, kase := range kases {
- assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses), "statuses: %v", kase.statuses)
+ assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
}
}
@@ -208,7 +208,7 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
Creator: user2,
SHA: commit.ID,
CommitStatus: &git_model.CommitStatus{
- State: commitstatus.CommitStatusFailure,
+ State: structs.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: commitstatus.CommitStatusSuccess,
+ State: structs.CommitStatusSuccess,
TargetURL: "https://example.com/tests/",
Context: "compliance/lint-backend",
},
@@ -256,26 +256,3 @@ 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 e4fa2b446a..bb6361050a 100644
--- a/models/git/lfs.go
+++ b/models/git/lfs.go
@@ -112,6 +112,7 @@ 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"`
}
@@ -145,6 +146,7 @@ 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 55bbe6938c..a3caed73c4 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.SplitSeq(strings.ToLower(filePatterns), ";") {
+ for _, expr := range strings.Split(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.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
+ teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
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 298c2fa074..a46402c543 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.Equal(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
+ assert.EqualValues(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
}
}
}
diff --git a/models/git/protected_branch_test.go b/models/git/protected_branch_test.go
index 367992081d..e1c91d927d 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.Equal(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
+ assert.EqualValues(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 9bef96d0dd..729fc1b9e5 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -9,7 +9,6 @@ import (
"context"
"fmt"
"html/template"
- "slices"
"strconv"
"unicode/utf8"
@@ -20,6 +19,8 @@ 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"
@@ -111,8 +112,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/PullRequest
- CommentTypeUnpin // 37 unpin Issue/PullRequest
+ CommentTypePin // 36 pin Issue
+ CommentTypeUnpin // 37 unpin Issue
CommentTypeChangeTimeEstimate // 38 Change time estimate
)
@@ -197,7 +198,12 @@ func (t CommentType) HasMailReplySupport() bool {
}
func (t CommentType) CountedAsConversation() bool {
- return slices.Contains(ConversationCountedCommentType(), t)
+ for _, ct := range ConversationCountedCommentType() {
+ if t == ct {
+ return true
+ }
+ }
+ return false
}
// ConversationCountedCommentType returns the comment types that are counted as a conversation
@@ -600,26 +606,26 @@ func (c *Comment) LoadAttachments(ctx context.Context) error {
return nil
}
-// UpdateCommentAttachments update attachments by UUIDs for the comment
-func UpdateCommentAttachments(ctx context.Context, c *Comment, uuids []string) error {
- if len(uuids) == 0 {
- 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
}
- 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)
+ 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)
}
- 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
- })
+ }
+ return committer.Commit()
}
// LoadAssigneeUserAndTeam if comment.Type is CommentTypeAssignees, then load assignees
@@ -768,6 +774,41 @@ 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)
@@ -851,7 +892,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
// Check comment type.
switch opts.Type {
case CommentTypeCode:
- if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
+ if err = updateAttachments(ctx, opts, comment); err != nil {
return err
}
if comment.ReviewID != 0 {
@@ -871,7 +912,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
}
fallthrough
case CommentTypeReview:
- if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
+ if err = updateAttachments(ctx, opts, comment); err != nil {
return err
}
case CommentTypeReopen, CommentTypeClose:
@@ -883,6 +924,23 @@ 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
diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go
index 55e67a1243..b562aab500 100644
--- a/models/issues/comment_code.go
+++ b/models/issues/comment_code.go
@@ -5,7 +5,6 @@ package issues
import (
"context"
- "strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/renderhelper"
@@ -115,9 +114,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
}
var err error
- rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
- FootnoteContextID: strconv.FormatInt(comment.ID, 10),
- })
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
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 f6c485449f..61ac1c8f56 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 := user_model.GetUsersMapByIDs(ctx, posterIDs)
+ posterMaps, err := getPostersByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, comment := range comments {
if comment.Poster == nil {
- comment.Poster = user_model.GetPossibleUserFromMap(comment.PosterID, posterMaps)
+ comment.Poster = getPoster(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 && comment.Label == nil
+ return comment.LabelID, comment.LabelID > 0
})
}
@@ -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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", labelIDs[:limit]).
Rows(new(Label))
@@ -104,7 +104,10 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -115,8 +118,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:]
}
- for _, comment := range comments {
- comment.Milestone = milestoneMaps[comment.MilestoneID]
+ for _, issue := range comments {
+ issue.Milestone = milestoneMaps[issue.MilestoneID]
}
return nil
}
@@ -140,7 +143,10 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -169,13 +175,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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", assigneeIDs[:limit]).
Rows(new(user_model.User))
@@ -244,7 +250,10 @@ func (comments CommentList) LoadIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -292,13 +301,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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := e.
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -374,7 +383,10 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
commentsIDs := comments.getAttachmentCommentIDs()
left := len(commentsIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("comment_id", commentsIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -415,9 +427,6 @@ 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 c08e3b970d..cb31a21f92 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.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)
+ 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)
unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB
@@ -45,24 +45,6 @@ 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())
@@ -111,7 +93,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, err)
issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- assert.Equal(t, issue.NumComments+1, issueModified.NumComments)
+ assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
}
@@ -122,5 +104,5 @@ func Test_UpdateIssueNumComments(t *testing.T) {
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)
+ assert.EqualValues(t, 1, issue2.NumComments)
}
diff --git a/models/issues/dependency_test.go b/models/issues/dependency_test.go
index 67418039de..6eed483cc9 100644
--- a/models/issues/dependency_test.go
+++ b/models/issues/dependency_test.go
@@ -49,13 +49,9 @@ func TestCreateIssueDependency(t *testing.T) {
assert.False(t, left)
// Close #2 and check again
- _, err = issues_model.CloseIssue(db.DefaultContext, issue2, user1)
+ _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true)
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)
@@ -63,11 +59,4 @@ 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 a86d50ca9d..f4b575d804 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -10,7 +10,6 @@ import (
"html/template"
"regexp"
"slices"
- "strconv"
"code.gitea.io/gitea/models/db"
project_model "code.gitea.io/gitea/models/project"
@@ -48,6 +47,23 @@ 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
@@ -63,6 +79,22 @@ 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.
@@ -98,7 +130,7 @@ type Issue struct {
// TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl"
Ref string
- PinOrder int `xorm:"-"` // 0 means not loaded, -1 means loaded but not pinned
+ PinOrder int `xorm:"DEFAULT 0"`
DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"`
@@ -240,9 +272,6 @@ 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
}
@@ -292,23 +321,6 @@ 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 {
@@ -348,10 +360,6 @@ 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
}
@@ -364,14 +372,6 @@ 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
@@ -596,9 +596,6 @@ 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
@@ -753,6 +750,190 @@ 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)
@@ -816,7 +997,7 @@ func ChangeIssueTimeEstimate(ctx context.Context, issue *Issue, doer *user_model
Doer: doer,
Repo: issue.Repo,
Issue: issue,
- Content: strconv.FormatInt(timeEstimate, 10),
+ Content: fmt.Sprintf("%d", timeEstimate),
}
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 f082079e07..10fc821454 100644
--- a/models/issues/issue_label.go
+++ b/models/issues/issue_label.go
@@ -206,7 +206,6 @@ 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 26b93189b8..22a4548adc 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -42,7 +42,10 @@ 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 := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Find(&repoMaps)
@@ -78,19 +81,53 @@ func (issues IssueList) LoadPosters(ctx context.Context) error {
return issue.PosterID, issue.Poster == nil && issue.PosterID > 0
})
- posterMaps, err := user_model.GetUsersMapByIDs(ctx, posterIDs)
+ posterMaps, err := getPostersByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, issue := range issues {
if issue.Poster == nil {
- issue.Poster = user_model.GetPossibleUserFromMap(issue.PosterID, posterMaps)
+ issue.Poster = getPoster(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 {
@@ -113,7 +150,10 @@ func (issues IssueList) LoadLabels(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).Table("label").
Join("LEFT", "issue_label", "issue_label.label_id = label.id").
In("issue_label.issue_id", issueIDs[:limit]).
@@ -165,7 +205,10 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -194,7 +237,10 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
}
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
projects := make([]*projectWithIssueID, 0, limit)
err := db.GetEngine(ctx).
@@ -233,7 +279,10 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
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()).
@@ -291,7 +340,10 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error {
pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
left := len(issuesIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(PullRequest))
@@ -336,7 +388,10 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) {
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -378,7 +433,10 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
rows, err := db.GetEngine(ctx).Table("comment").
Join("INNER", "issue", "issue.id = comment.issue_id").
In("issue.id", issuesIDs[:limit]).
@@ -442,7 +500,10 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
left := len(ids)
for left > 0 {
- limit := min(left, db.DefaultMaxInSize)
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
// select issue_id, sum(time) from tracked_time where issue_id in () group by issue_id
rows, err := db.GetEngine(ctx).Table("tracked_time").
@@ -479,39 +540,6 @@ 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 5b4d2ca5ab..9069e1012d 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.Equal(t, issue.RepoID, issue.Repo.ID)
+ assert.EqualValues(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.Equal(t, issue.RepoID, issue.Repo.ID)
+ assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.Equal(t, issue.RepoID, label.RepoID)
+ assert.EqualValues(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.Equal(t, issue.PosterID, issue.Poster.ID)
+ assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
+ assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.Equal(t, issue.ID, attachment.IssueID)
+ assert.EqualValues(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.Equal(t, issue.ID, comment.IssueID)
+ assert.EqualValues(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 fa0d128f74..b21629b529 100644
--- a/models/issues/issue_lock.go
+++ b/models/issues/issue_lock.go
@@ -12,14 +12,8 @@ import (
// IssueLockOptions defines options for locking and/or unlocking an issue/PR
type IssueLockOptions struct {
- 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.
+ Doer *user_model.User
+ Issue *Issue
Reason string
}
diff --git a/models/issues/issue_pin.go b/models/issues/issue_pin.go
deleted file mode 100644
index ae6195b05d..0000000000
--- a/models/issues/issue_pin.go
+++ /dev/null
@@ -1,246 +0,0 @@
-// 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_search.go b/models/issues/issue_search.go
index 79bd6a19b0..694b918755 100644
--- a/models/issues/issue_search.go
+++ b/models/issues/issue_search.go
@@ -21,16 +21,14 @@ import (
"xorm.io/xorm"
)
-const ScopeSortPrefix = "scope-"
-
// IssuesOptions represents options of an issue.
-type IssuesOptions struct { //nolint:revive // export stutter
+type IssuesOptions struct { //nolint
Paginator *db.ListOptions
RepoIDs []int64 // overwrites RepoCond if the length is not 0
AllPublic bool // include also all public repositories
RepoCond builder.Cond
- AssigneeID string // "(none)" or "(any)" or a user ID
- PosterID string // "(none)" or "(any)" or a user ID
+ AssigneeID optional.Option[int64]
+ PosterID optional.Option[int64]
MentionedID int64
ReviewRequestedID int64
ReviewedID int64
@@ -72,24 +70,11 @@ 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":
@@ -371,25 +356,26 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, owner *user_mod
return cond
}
-func applyAssigneeCondition(sess *xorm.Session, assigneeID string) {
+func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) {
// old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64
- if assigneeID == "(none)" {
+ if !assigneeID.Has() || assigneeID.Value() == 0 {
+ return
+ }
+ if assigneeID.Value() == db.NoConditionID {
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
- } 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 {
+ } else {
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
- And("issue_assignees.assignee_id = ?", assigneeIDInt64)
+ And("issue_assignees.assignee_id = ?", assigneeID.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)
+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())
}
}
diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go
index adedaa3d3a..9ef9347a16 100644
--- a/models/issues/issue_stats.go
+++ b/models/issues/issue_stats.go
@@ -94,7 +94,10 @@ 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 := min(i+MaxQueryParameters, len(opts.IssueIDs))
+ chunk := i + MaxQueryParameters
+ if chunk > len(opts.IssueIDs) {
+ chunk = len(opts.IssueIDs)
+ }
stats, err := getIssueStatsChunk(ctx, opts, opts.IssueIDs[i:chunk])
if err != nil {
return nil, err
@@ -104,7 +107,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
accum.YourRepositoriesCount += stats.YourRepositoriesCount
accum.AssignCount += stats.AssignCount
accum.CreateCount += stats.CreateCount
- accum.MentionCount += stats.MentionCount
+ accum.OpenCount += 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 1c5db55bbc..dbbb1e4179 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,6 +16,7 @@ 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"
@@ -142,8 +143,8 @@ func TestUpdateIssueCols(t *testing.T) {
then := time.Now().Unix()
updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
- assert.Equal(t, newTitle, updatedIssue.Title)
- assert.Equal(t, prevContent, updatedIssue.Content)
+ assert.EqualValues(t, newTitle, updatedIssue.Title)
+ assert.EqualValues(t, prevContent, updatedIssue.Content)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
@@ -155,7 +156,7 @@ func TestIssues(t *testing.T) {
}{
{
issues_model.IssuesOptions{
- AssigneeID: "1",
+ AssigneeID: optional.Some(int64(1)),
SortType: "oldest",
},
[]int64{1, 6},
@@ -202,7 +203,7 @@ func TestIssues(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
for i, issue := range issues {
- assert.Equal(t, test.ExpectedIssueIDs[i], issue.ID)
+ assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
}
}
}
@@ -235,10 +236,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.Equal(t, issue.Title, newIssue.Title)
- assert.Equal(t, issue.Content, newIssue.Content)
+ assert.EqualValues(t, issue.Title, newIssue.Title)
+ assert.EqualValues(t, issue.Content, newIssue.Content)
if expectIndex > 0 {
- assert.Equal(t, expectIndex, newIssue.Index)
+ assert.EqualValues(t, expectIndex, newIssue.Index)
}
})
return &newIssue
@@ -271,8 +272,8 @@ func TestIssue_ResolveMentions(t *testing.T) {
for i, user := range resolved {
ids[i] = user.ID
}
- slices.Sort(ids)
- assert.Equal(t, expected, ids)
+ sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
+ assert.EqualValues(t, expected, ids)
}
// Public repo, existing user
@@ -293,7 +294,7 @@ func TestResourceIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
var wg sync.WaitGroup
- for i := range 100 {
+ for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0)
@@ -315,7 +316,7 @@ func TestCorrectIssueStats(t *testing.T) {
issueAmount := issues_model.MaxQueryParameters + 10
var wg sync.WaitGroup
- for i := range issueAmount {
+ for i := 0; i < issueAmount; i++ {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
@@ -325,7 +326,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(t.Context(), &issues_model.IssuesOptions{
+ issues, err := issues_model.Issues(context.TODO(), &issues_model.IssuesOptions{
Paginator: &db.ListOptions{
PageSize: issueAmount,
},
@@ -393,28 +394,28 @@ func TestIssueLoadAttributes(t *testing.T) {
for _, issue := range issueList {
assert.NoError(t, issue.LoadAttributes(db.DefaultContext))
- assert.Equal(t, issue.RepoID, issue.Repo.ID)
+ assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.Equal(t, issue.RepoID, label.RepoID)
+ assert.EqualValues(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.Equal(t, issue.PosterID, issue.Poster.ID)
+ assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
+ assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.Equal(t, issue.ID, attachment.IssueID)
+ assert.EqualValues(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.Equal(t, issue.ID, comment.IssueID)
+ assert.EqualValues(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 9b99787e3b..5b929c9115 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -5,14 +5,16 @@ 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"
@@ -26,40 +28,38 @@ import (
// UpdateIssueCols updates cols of issue
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
- _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue)
- return err
+ if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
+ return err
+ }
+ return nil
}
-// ErrIssueIsClosed is used when close a closed issue
-type ErrIssueIsClosed struct {
- ID int64
- RepoID int64
- Index int64
- IsPull bool
-}
+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
+ }
-// 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,
+ // 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,
}
}
+ 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.Repo.IsDependenciesEnabled(ctx) {
+ if issue.IsClosed && 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,63 +71,16 @@ func SetIssueAsClosed(ctx context.Context, issue *Issue, doer *user_model.User,
}
}
- issue.IsClosed = true
- issue.ClosedUnix = timeutil.TimeStampNow()
+ if issue.IsClosed {
+ issue.ClosedUnix = timeutil.TimeStampNow()
+ } else {
+ issue.ClosedUnix = 0
+ }
- if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
- Where("is_closed = ?", false).
- Update(issue); err != nil {
+ if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); 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
@@ -150,6 +103,14 @@ func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User
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,
@@ -158,8 +119,8 @@ func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User
})
}
-// CloseIssue changes issue status to closed.
-func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
+// ChangeIssueStatus changes issue status to open or closed.
+func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) {
if err := issue.LoadRepo(ctx); err != nil {
return nil, err
}
@@ -167,45 +128,7 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
return nil, err
}
- 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
+ return changeIssueStatus(ctx, issue, doer, isClosed, false)
}
// ChangeIssueTitle changes the title of this issue, as the given user.
@@ -216,7 +139,7 @@ func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User,
}
defer committer.Close()
- issue.Title = util.EllipsisDisplayString(issue.Title, 255)
+ issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
return fmt.Errorf("updateIssueCols: %w", err)
}
@@ -304,7 +227,7 @@ func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string)
if err != nil {
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
- for i := range attachments {
+ for i := 0; i < len(attachments); i++ {
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)
@@ -384,10 +307,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
}
if opts.Issue.Index <= 0 {
- return errors.New("no issue index provided")
+ return fmt.Errorf("no issue index provided")
}
if opts.Issue.ID > 0 {
- return errors.New("issue exist")
+ return fmt.Errorf("issue exist")
}
if _, err := e.Insert(opts.Issue); err != nil {
@@ -444,10 +367,19 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
return err
}
- if err := UpdateIssueAttachments(ctx, opts.Issue.ID, opts.Attachments); err != nil {
- 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 = opts.Issue.LoadAttributes(ctx); err != nil {
return err
}
@@ -470,7 +402,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, la
}
issue.Index = idx
- issue.Title = util.EllipsisDisplayString(issue.Title, 255)
+ issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
@@ -609,7 +541,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
unittype = unit.TypePullRequests
}
for _, team := range teams {
- if team.HasAdminAccess() {
+ if team.AccessMode >= perm.AccessModeAdmin {
checked = append(checked, team.ID)
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
continue
@@ -713,13 +645,137 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
return 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
+// 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
+ }
}
- return repoIDs, nil
+
+ 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
}
diff --git a/models/issues/issue_user_test.go b/models/issues/issue_user_test.go
index 7c21aa15ee..ce47adb53a 100644
--- a/models/issues/issue_user_test.go
+++ b/models/issues/issue_user_test.go
@@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func Test_NewIssueUsers(t *testing.T) {
@@ -28,8 +27,9 @@ func Test_NewIssueUsers(t *testing.T) {
}
// artificially insert new issue
- require.NoError(t, db.Insert(db.DefaultContext, newIssue))
- require.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
+ unittest.AssertSuccessfulInsert(t, newIssue)
+
+ assert.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 7f257330b7..f1b1bb2a6b 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.CloseIssue(db.DefaultContext, i3, d)
+ _, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true)
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 cfbe100926..b9d24bbe99 100644
--- a/models/issues/label.go
+++ b/models/issues/label.go
@@ -87,7 +87,6 @@ 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
@@ -237,7 +236,7 @@ func UpdateLabel(ctx context.Context, l *Label) error {
}
l.Color = color
- return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "exclusive_order", "archived_unix")
+ return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "archived_unix")
}
// DeleteLabel delete a label
@@ -300,9 +299,6 @@ 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").
@@ -379,9 +375,6 @@ 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).
@@ -454,9 +447,6 @@ 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 226036d543..1d4b6f4684 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.Equal(t, 2, label.NumOpenIssues)
+ assert.EqualValues(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.Equal(t, expectedIssueIDs[i], label.ID)
+ assert.EqualValues(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.Equal(t, expectedIssueIDs[i], label.ID)
+ assert.EqualValues(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.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, 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.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.Equal(t, prevNumIssues+1, label.NumIssues)
+ assert.EqualValues(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.Equal(t, 3, label1.NumIssues)
- assert.Equal(t, 1, label1.NumClosedIssues)
+ assert.EqualValues(t, 3, label1.NumIssues)
+ assert.EqualValues(t, 1, label1.NumClosedIssues)
label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
- assert.Equal(t, 1, label2.NumIssues)
- assert.Equal(t, 1, label2.NumClosedIssues)
+ assert.EqualValues(t, 1, label2.NumIssues)
+ assert.EqualValues(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.GetBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) != nil {
+ if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) {
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.Equal(t, expectedNumIssues, label.NumIssues)
- assert.Equal(t, expectedNumClosedIssues, label.NumClosedIssues)
+ assert.EqualValues(t, expectedNumIssues, label.NumIssues)
+ assert.EqualValues(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 f73355c27d..28cd0c028b 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.Equal(t, repoID, milestone.RepoID)
+ assert.EqualValues(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.Equal(t, "newMilestoneName", milestone.Name)
+ assert.EqualValues(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.Equal(t, repo.NumMilestones+1, repoModified.NumMilestones)
+ assert.EqualValues(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 0ff32e2473..319ead5dbd 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,6 +80,22 @@ 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
@@ -103,6 +119,27 @@ 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
@@ -462,6 +499,65 @@ 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)
@@ -476,7 +572,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss
}
issue.Index = idx
- issue.Title = util.EllipsisDisplayString(issue.Title, 255)
+ issue.Title, _ = util.SplitStringAtByteN(issue.Title, 255)
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
@@ -649,6 +745,12 @@ 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)
@@ -705,7 +807,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 errors.New("pull ID is 0")
+ return fmt.Errorf("pull ID is 0")
}
pr.CommitsAhead = ahead
pr.CommitsBehind = behind
@@ -898,7 +1000,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
if strings.Contains(user, "/") {
s := strings.Split(user, "/")
if len(s) != 2 {
- warnings = append(warnings, "incorrect codeowner group: "+user)
+ warnings = append(warnings, fmt.Sprintf("incorrect codeowner group: %s", user))
continue
}
orgName := s[0]
@@ -906,12 +1008,12 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
org, err := org_model.GetOrgByName(ctx, orgName)
if err != nil {
- warnings = append(warnings, "incorrect codeowner organization: "+user)
+ warnings = append(warnings, fmt.Sprintf("incorrect codeowner organization: %s", user))
continue
}
teams, err := org.LoadTeams(ctx)
if err != nil {
- warnings = append(warnings, "incorrect codeowner team: "+user)
+ warnings = append(warnings, fmt.Sprintf("incorrect codeowner team: %s", user))
continue
}
@@ -923,7 +1025,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
} else {
u, err := user_model.GetUserByName(ctx, user)
if err != nil {
- warnings = append(warnings, "incorrect codeowner user: "+user)
+ warnings = append(warnings, fmt.Sprintf("incorrect codeowner user: %s", user))
continue
}
rule.Users = append(rule.Users, u)
diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index 84f9f6166d..59010aa9d0 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -28,16 +28,11 @@ 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":
@@ -61,7 +56,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) (PullRequestList, error) {
+func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
prs := make([]*PullRequest, 0, 2)
sess := db.GetEngine(ctx).
Join("INNER", "issue", "issue.id = pull_request.issue_id").
@@ -116,7 +111,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) (PullRequestList, error) {
+func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, 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=?",
@@ -152,8 +147,7 @@ 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)
- found := findSession.Find(&prs)
- return prs, maxResults, found
+ return prs, maxResults, findSession.Find(&prs)
}
// PullRequestList defines a list of pull requests
@@ -172,23 +166,6 @@ 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 eb2de006d6..c7a898ca4e 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.PullRequestList{
+ prs := []*issues_model.PullRequest{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- assert.NoError(t, prs.LoadAttributes(db.DefaultContext))
+ assert.NoError(t, issues_model.PullRequestList(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.PullRequestList{
+ prs := []*issues_model.PullRequest{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewComments, err := prs.LoadReviewCommentsCounts(db.DefaultContext)
+ reviewComments, err := issues_model.PullRequestList(prs).LoadReviewCommentsCounts(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, reviewComments, 2)
for _, pr := range prs {
- assert.Equal(t, 1, reviewComments[pr.IssueID])
+ assert.EqualValues(t, 1, reviewComments[pr.IssueID])
}
}
func TestPullRequestList_LoadReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := issues_model.PullRequestList{
+ prs := []*issues_model.PullRequest{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewList, err := prs.LoadReviews(db.DefaultContext)
+ reviewList, err := issues_model.PullRequestList(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 39efaa5792..090659864a 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -14,7 +14,6 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestPullRequest_LoadAttributes(t *testing.T) {
@@ -77,47 +76,6 @@ 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())
@@ -248,6 +206,19 @@ 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{
@@ -314,7 +285,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.Equal(t, countBefore, countAfter)
+ assert.EqualValues(t, countBefore, countAfter)
}
func TestParseCodeOwnersLine(t *testing.T) {
@@ -347,7 +318,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.Equal(t, expected, approvers)
+ assert.EqualValues(t, expected, approvers)
}
func TestGetPullRequestByMergedCommit(t *testing.T) {
diff --git a/models/issues/reaction.go b/models/issues/reaction.go
index f24001fd23..11b3c6be20 100644
--- a/models/issues/reaction.go
+++ b/models/issues/reaction.go
@@ -7,7 +7,6 @@ import (
"bytes"
"context"
"fmt"
- "strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -322,11 +321,6 @@ 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 {
@@ -344,7 +338,7 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
- reaction.User = newMigrationOriginalUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
+ reaction.User = user_model.NewReplaceUser(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 71fdb7456f..3e787273be 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -5,7 +5,6 @@ package issues
import (
"context"
- "errors"
"fmt"
"slices"
"strings"
@@ -375,7 +374,7 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
- return nil, errors.New("provide either reviewer or reviewer team")
+ return nil, fmt.Errorf("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil {
@@ -664,7 +663,7 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
}
if review != nil {
- // skip it when reviewer has been request to review
+ // skip it when reviewer hase 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.
}
@@ -931,19 +930,17 @@ 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 , official reviewer and poster can do it
+// the PR writer , offfcial 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, errors.New("issue or doer is nil")
+ return false, fmt.Errorf("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
@@ -973,11 +970,11 @@ func DeleteReview(ctx context.Context, r *Review) error {
defer committer.Close()
if r.ID == 0 {
- return errors.New("review is not allowed to be 0")
+ return fmt.Errorf("review is not allowed to be 0")
}
if r.Type == ReviewTypeRequest {
- return errors.New("review request can not be deleted using this method")
+ return fmt.Errorf("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 bbb8c489fa..928f24fb2d 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -22,7 +22,7 @@ type ReviewList []*Review
// LoadReviewers loads reviewers
func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
reviewerIDs := make([]int64, len(reviews))
- for i := range reviews {
+ for i := 0; i < len(reviews); i++ {
reviewerIDs[i] = reviews[i].ReviewerID
}
reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs)
diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go
index 761b8f91a0..baffe04ace 100644
--- a/models/issues/stopwatch.go
+++ b/models/issues/stopwatch.go
@@ -5,6 +5,7 @@ package issues
import (
"context"
+ "fmt"
"time"
"code.gitea.io/gitea/models/db"
@@ -14,6 +15,20 @@ 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"`
@@ -31,6 +46,11 @@ 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.SecToHours(s.Seconds())
+}
+
func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
sw = new(Stopwatch)
exists, err = db.GetEngine(ctx).
@@ -40,11 +60,13 @@ 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 {
@@ -70,7 +92,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
return res, nil
}
-// GetUserStopwatches return list of the user's all stopwatches
+// GetUserStopwatches return list of all stopwatches of a user
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)
@@ -85,7 +107,7 @@ func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOp
return sws, nil
}
-// CountUserStopwatches return count of the user's all stopwatches
+// CountUserStopwatches return count of all stopwatches of a user
func CountUserStopwatches(ctx context.Context, userID int64) (int64, error) {
return db.GetEngine(ctx).Where("user_id = ?", userID).Count(&Stopwatch{})
}
@@ -119,21 +141,43 @@ func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopw
return exists, sw, issue, err
}
-// 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)
+// 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 false, err
- } else if !exists {
- return false, nil
+ return err
}
- if err = finishIssueStopwatch(ctx, user, issue, sw); err != nil {
- return false, err
+ if !exists {
+ return nil
}
- return true, nil
+ return FinishIssueStopwatch(ctx, user, issue)
}
-func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue, sw *Stopwatch) error {
+// 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 {
+ sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return ErrIssueStopwatchNotExist{
+ UserID: user.ID,
+ IssueID: issue.ID,
+ }
+ }
+
// Create tracked time out of the time difference between start date and actual date
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
@@ -145,12 +189,14 @@ func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
Time: timediff,
}
- if err := issue.LoadRepo(ctx); err != nil {
- return err
- }
if err := db.Insert(ctx, tt); err != nil {
return err
}
+
+ if err := issue.LoadRepo(ctx); err != nil {
+ return err
+ }
+
if _, err := CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
@@ -161,65 +207,83 @@ func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
}); err != nil {
return err
}
- _, err := db.DeleteByBean(ctx, sw)
+ _, err = db.DeleteByBean(ctx, sw)
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
- }
+// 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
}
}
- if err = issue.LoadRepo(ctx); err != nil {
- return false, err
+ // Create stopwatch
+ sw := &Stopwatch{
+ UserID: user.ID,
+ IssueID: issue.ID,
}
- if err = db.Insert(ctx, &Stopwatch{UserID: user.ID, IssueID: issue.ID}); err != nil {
- return false, err
+
+ if err := db.Insert(ctx, sw); err != nil {
+ return err
}
- if _, err = CreateComment(ctx, &CreateCommentOptions{
+
+ if err := issue.LoadRepo(ctx); err != nil {
+ return err
+ }
+
+ if _, err := CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
Type: CommentTypeStartTracking,
}); err != nil {
- return false, err
+ return err
}
- return true, nil
+
+ return nil
}
// CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
-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 {
+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 {
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 = e.Delete(sw); err != nil {
- return err
- }
- if _, err = CreateComment(ctx, &CreateCommentOptions{
+
+ if _, err := CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
@@ -227,8 +291,6 @@ func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (
}); err != nil {
return err
}
- ok = true
- return nil
- })
- return ok, err
+ }
+ return nil
}
diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go
index 6333c10234..a1bf9dc931 100644
--- a/models/issues/stopwatch_test.go
+++ b/models/issues/stopwatch_test.go
@@ -10,6 +10,7 @@ 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"
)
@@ -17,22 +18,26 @@ import (
func TestCancelStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
-
- ok, err := issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
+ user1, err := user_model.GetUserByID(db.DefaultContext, 1)
+ assert.NoError(t, err)
+
+ 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)
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})
- ok, err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
- assert.NoError(t, err)
- assert.False(t, ok)
+ _ = 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))
}
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))
}
@@ -53,35 +58,21 @@ func TestHasUserStopwatch(t *testing.T) {
func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- 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})
+ user2, err := user_model.GetUserByID(db.DefaultContext, 2)
+ assert.NoError(t, err)
+ org3, err := user_model.GetUserByID(db.DefaultContext, 3)
+ assert.NoError(t, err)
- // create a new stopwatch
- ok, err := issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1)
+ issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
assert.NoError(t, err)
- 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)
+ issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
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})
- // 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)
+ 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})
}
diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go
index 2afbe272ed..ea404d36cd 100644
--- a/models/issues/tracked_time.go
+++ b/models/issues/tracked_time.go
@@ -350,7 +350,10 @@ 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 := min(i+MaxQueryParameters, len(opts.IssueIDs))
+ chunk := i + MaxQueryParameters
+ if chunk > len(opts.IssueIDs) {
+ chunk = 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 479a46379c..eb1c44a79e 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 := "tmp_recreate__" + tableName
+ tempTableName := fmt.Sprintf("tmp_recreate__%s", 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 errors.New("no columns in new table")
+ return fmt.Errorf("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 range 20 {
+ for i := 0; i < 20; i++ {
err = os.RemoveAll(dir)
if err == nil {
break
@@ -552,11 +552,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
+ if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
return err
}
- if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
+ if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
return err
}
return nil
@@ -568,11 +568,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
+ if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
return err
}
- if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
+ if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", 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("CREATE SCHEMA " + setting.Database.Schema)
+ _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
if err != nil {
return err
}
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index 33fd1df707..c2134f702a 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -1,6 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
+//nolint:forbidigo
package base
import (
@@ -12,10 +13,9 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/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(); err != nil {
+ if err := unittest.LoadFixtures(x); err != nil {
t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
return x, deferFn
}
@@ -92,7 +92,10 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
func MainTest(m *testing.M) {
testlogger.Init()
- giteaRoot := test.SetupGiteaRoot()
+ giteaRoot := base.SetupGiteaRoot()
+ if giteaRoot == "" {
+ testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
+ }
giteaBinary := "gitea"
if runtime.GOOS == "windows" {
giteaBinary += ".exe"
@@ -105,7 +108,7 @@ func MainTest(m *testing.M) {
giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" {
giteaConf = filepath.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
- _, _ = fmt.Fprintf(os.Stderr, "Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
+ fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
}
if !filepath.IsAbs(giteaConf) {
@@ -114,16 +117,15 @@ func MainTest(m *testing.M) {
setting.CustomConf = giteaConf
}
- tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
+ tmpDataPath, err := os.MkdirTemp("", "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.InitSettingsForTesting()
+ unittest.InitSettings()
if err = git.InitFull(context.Background()); err != nil {
testlogger.Fatalf("Unable to InitFull: %v\n", err)
}
@@ -133,7 +135,10 @@ func MainTest(m *testing.M) {
exitStatus := m.Run()
if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
- _, _ = fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ }
+ if err := removeAllWithRetry(tmpDataPath); err != nil {
+ 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 176372486e..52d10c4fe8 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -6,7 +6,6 @@ package migrations
import (
"context"
- "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/v1_10"
@@ -23,7 +22,6 @@ 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"
@@ -371,17 +369,6 @@ 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
}
@@ -420,14 +407,14 @@ func ExpectedDBVersion() int64 {
}
// EnsureUpToDate will check if the db is at the correct version
-func EnsureUpToDate(ctx context.Context, x *xorm.Engine) error {
+func EnsureUpToDate(x *xorm.Engine) error {
currentDB, err := GetCurrentDBVersion(x)
if err != nil {
return err
}
if currentDB < 0 {
- return errors.New("database has not been initialized")
+ return fmt.Errorf("database has not been initialized")
}
if minDBVersion > currentDB {
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index 8649d116f5..e66b015b3d 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.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))
+ 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))
}
diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go
index 1742bea296..5d2fd8e244 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
+package v1_10 //nolint
import (
"net/url"
diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go
index 6c8dfe2486..f023a2a0e7 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
+package v1_10 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go
index eb8e81c19e..7e86ac364f 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
+package v1_10 //nolint
import (
"crypto/sha1"
diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go
index 0df2a6e17b..d5f27ffdc6 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go
index 5521a97e32..295d4b1c1b 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go
index 08db6c2742..48cac2de70 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go
index b6c04a9234..9080108594 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
+package v1_10 //nolint
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go
index c131be9a8d..ee59a8db39 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go
index 13b7d7b303..c131af162b 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go
index 86b52026bf..3b1f67fd9c 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go
index ca35a169c4..34c8240031 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
+package v1_10 //nolint
import (
"path/filepath"
diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go
index 5872bb63e5..dee45b32e3 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go
index d21c326459..bdd9aed089 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
+package v1_10 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go
index 223c188057..ebe6597f7c 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
+package v1_10 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go
index e52290afb0..9358e4cef3 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
+package v1_11 //nolint
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 a515710160..53527dac58 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go
index 3b0d3c64b2..3e8ee64bc1 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
+package v1_11 //nolint
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 d86973a0f6..b91340c30a 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go
index edffe18683..ecb11cdd1e 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go
index a158e3bb50..f0bfe5862c 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go
index 8f14504ceb..a85096234d 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go
index f7616aec7b..ea565ccda3 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go
index 512f728c03..81afa1331d 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go
index c27465f051..ff108479a9 100644
--- a/models/migrations/v1_11/v111.go
+++ b/models/migrations/v1_11/v111.go
@@ -1,11 +1,10 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11
+package v1_11 //nolint
import (
"fmt"
- "slices"
"xorm.io/xorm"
)
@@ -345,8 +344,10 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
}
return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil
}
- if slices.Contains(protectedBranch.ApprovalsWhitelistUserIDs, reviewer.ID) {
- return true, nil
+ for _, id := range protectedBranch.ApprovalsWhitelistUserIDs {
+ if id == reviewer.ID {
+ return true, nil
+ }
}
// isUserInTeams
diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go
index fe45cf9222..0857663119 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
+package v1_11 //nolint
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 {
- log.Warn("Unable to remove attachment file by UUID %s: %v", uuid, err)
+ fmt.Printf("Error: %v", err) //nolint:forbidigo
}
}
diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go
index a4d54f66fb..dea344a44f 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
+package v1_11 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go
index 9467a8a90c..95adcee989 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
+package v1_11 //nolint
import (
"net/url"
diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go
index 5933c0520f..8c631cfd0b 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
+package v1_11 //nolint
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(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data))))
+ newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%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 729fbad18b..85aa76c1e0 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
+package v1_11 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go
index 73b58ca34b..8eadcdef2b 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go
index e8b4249743..eb022dc5e4 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go
index b4bf29a935..60bfe6a57d 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go
index 14d515f5a7..3f7ed8d373 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go
index a28ae4e1c9..175ec9164d 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
+package v1_12 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go
index bc1b175f6a..6e31d863a1 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go
index 52b10bb850..b0c3af07a3 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go
index 9a93f436d4..d2ba03ffe0 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go
index 7f582ecff5..ec4ffaab25 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go
index 64fd7f7478..ca9ec3aa3f 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
+package v1_12 //nolint
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go
index 9bd78db95e..00e391dc87 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go
index e7dbff3766..44d44a26c5 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
+package v1_12 //nolint
import (
"fmt"
@@ -82,17 +82,17 @@ func FixMergeBase(x *xorm.Engine) error {
if !pr.HasMerged {
var err error
- pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
- pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&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("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&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("merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(&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 3e4d3aca68..cf228242b9 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go
index 107bb756fd..391810c7ca 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
+package v1_12 //nolint
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go
index 1266c2f185..5184bc3590 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go
index 8b1ae6db93..3b2b28f7ab 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go
index 69e20597d8..c9087fc8c1 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
+package v1_12 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go
index 09d743964d..3d1c82f09e 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
+package v1_12 //nolint
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("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&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("merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(&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 5df0ad7fc4..8898011df5 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go
index 0f53278b46..d91ff92feb 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go
index 9d38483488..0d86b72010 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
+package v1_12 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go
index 4485adeb2d..8c8d353f40 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
+package v1_12 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go
index a3799841ac..279aa7df87 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
+package v1_12 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go
index a9a047bca9..2d3337012d 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
+package v1_13 //nolint
import (
"fmt"
@@ -21,7 +21,12 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
// RepoIndexerType specifies the repository indexer type
type RepoIndexerType int
- const RepoIndexerTypeStats RepoIndexerType = 1
+ const (
+ // RepoIndexerTypeCode code indexer - 0
+ RepoIndexerTypeCode RepoIndexerType = iota //nolint:unused
+ // RepoIndexerTypeStats repository stats indexer - 1
+ RepoIndexerTypeStats
+ )
// RepoIndexerStatus see models/repo_indexer.go
type RepoIndexerStatus struct {
@@ -41,7 +46,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
}
// Delete language stats
- if _, err := x.Exec(truncExpr + " language_stat"); err != nil {
+ if _, err := x.Exec(fmt.Sprintf("%s language_stat", truncExpr)); err != nil {
return err
}
diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go
index b54bc1727c..ae211e0e44 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
+package v1_13 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go
index d08a0ae0bf..7c7c01ad47 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
+package v1_13 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go
index b9a856ed0f..885768dff3 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
+package v1_13 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go
index 9352d78bc8..f5a0bc5751 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
+package v1_13 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go
index 86ebb4f9d9..8acb29bf33 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
+package v1_13 //nolint
import (
"fmt"
@@ -42,7 +42,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
switch {
case setting.Database.Type.IsMySQL():
- if _, err := sess.Exec("ALTER TABLE language_stat MODIFY COLUMN language " + sqlType); err != nil {
+ if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat MODIFY COLUMN language %s", 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("ALTER TABLE language_stat ALTER COLUMN language " + sqlType); err != nil {
+ if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language %s", 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("ALTER TABLE language_stat ALTER COLUMN language TYPE " + sqlType); err != nil {
+ if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language TYPE %s", sqlType)); err != nil {
return err
}
}
diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go
index 355c772c26..7d9a878704 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
+package v1_13 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go
index 0059c06220..510ef39b28 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
+package v1_13 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go
index d276db3d61..7bb8ab700b 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
+package v1_13 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go
index a96b8e5ac7..2a1db04cbb 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
+package v1_13 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go
index 590ea72903..d5ba489566 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
+package v1_13 //nolint
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 454929534f..25af1d03ec 100644
--- a/models/migrations/v1_13/v151.go
+++ b/models/migrations/v1_13/v151.go
@@ -1,11 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13
+package v1_13 //nolint
import (
"context"
- "errors"
"fmt"
"strings"
@@ -114,7 +113,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error {
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
- return errors.New("no columns in new table")
+ return fmt.Errorf("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 648e26446f..502c82a40d 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
+package v1_13 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go
index e5462fc162..0b2dd3eb62 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
+package v1_13 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go
index 5477d1b889..60cc56713e 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
+package v1_13 //nolint
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 978f88577c..7a091b9b9a 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
+package v1_14 //nolint
import (
"testing"
diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go
index 505a9ae033..e814f59938 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go
index 2fa5819610..2cf4954a15 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go
index 2c5625ebbd..7187278d29 100644
--- a/models/migrations/v1_14/v157.go
+++ b/models/migrations/v1_14/v157.go
@@ -1,13 +1,24 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14
+package v1_14 //nolint
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 3c57e8e3da..1094d8abf7 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
+package v1_14 //nolint
import (
- "errors"
+ "fmt"
"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 errors.New("Unsupported database type")
+ return fmt.Errorf("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 e6f6f0f061..149ae0f6a8 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
+package v1_14 //nolint
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 73f3798954..4dea91b514 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
+package v1_14 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go
index eb92dee77c..ac7e821a80 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
+package v1_14 //nolint
import (
"context"
diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go
index a0ddd36d55..2e4e0b8eb0 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
+package v1_14 //nolint
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 84c35190b7..0cd8ba68c8 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
+package v1_14 //nolint
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 d2fd9b8464..54f6951427 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go
index 6e1b34156b..926350cdf7 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
+package v1_14 //nolint
import (
"code.gitea.io/gitea/models/migrations/base"
@@ -16,7 +16,10 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return nil
}
- // HookTask: Typ string `xorm:"VARCHAR(16) index"`
+ type HookTask struct { //nolint:unused
+ Typ string `xorm:"VARCHAR(16) index"`
+ }
+
if err := base.ModifyColumn(x, "hook_task", &schemas.Column{
Name: "typ",
SQLType: schemas.SQLType{
@@ -39,7 +42,10 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return err
}
- // Webhook: Type string `xorm:"VARCHAR(16) index"`
+ type Webhook struct { //nolint:unused
+ 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 4c106bd7da..e5731582fd 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
+package v1_14 //nolint
import (
"crypto/sha256"
diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go
index d77bbc401e..9d416f6a32 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go
index aa93eec19b..a30a8859f7 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
+package v1_14 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go
index 4f9df0d96f..5b81bb58b1 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
+package v1_14 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go
index a2ff4623e1..7b6498a3e9 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go
index 7b200e960a..51a35a02ad 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go
index bbd61d87b2..0f9bef902a 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
+package v1_14 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go
index 7752fbe966..2d9eee9197 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go
index 4049e43070..c839e15db8 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go
index 92ed130473..70d72b2600 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go
index ef5dce9a02..1ed49f75fa 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
+package v1_14 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go
index 5c1db4db71..ea3e750d7f 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
+package v1_14 //nolint
import (
"testing"
diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go
index 96676bf8d9..6e1838f369 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
+package v1_14 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go
index 263f69f338..5568a18fec 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
+package v1_14 //nolint
import (
"testing"
diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go
index d01585e997..366f19788e 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
+package v1_15 //nolint
import (
"testing"
diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go
index ca3a5c262e..6d236eb049 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
+package v1_15 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go
index d6fb86ffec..f6b142eb42 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
+package v1_15 //nolint
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 dd132f8330..c71e771861 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
+package v1_15 //nolint
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go
index fb1d3d7a75..2185ed0213 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
+package v1_15 //nolint
import (
"strings"
diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go
index 73b5c1f3d6..1b075be7a0 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
+package v1_15 //nolint
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.Equal(t, user.IsActive, emailAddress.IsActivated)
- assert.Equal(t, user.ID, emailAddress.UID)
+ assert.EqualValues(t, user.IsActive, emailAddress.IsActivated)
+ assert.EqualValues(t, user.ID, emailAddress.UID)
}
}
diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go
index f53ff11df9..9ca500c0f9 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
+package v1_15 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go
index 5fc6a0c467..75ef8e1cd8 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
+package v1_15 //nolint
import (
"testing"
diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go
index 5d0582f03d..effad1b467 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
+package v1_15 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go
index 2823bc1f7a..4b3dd1467a 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
+package v1_15 //nolint
import (
"context"
diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go
index 60af59edca..e5878ec193 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
+package v1_15 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go
index 67dc97d13d..01aab3add5 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
+package v1_15 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go
index 5fd90c65fb..21cd6772b7 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
+package v1_15 //nolint
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 4494e6ff05..71e45cab0e 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
+package v1_15 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go
index 7f93d6e9e5..817a0c13a4 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
+package v1_16 //nolint
import (
"testing"
diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go
index 6bc99e58ab..5649645051 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
+package v1_16 //nolint
import (
"encoding/binary"
diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go
index fb56ac8e11..32ef821d27 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
+package v1_16 //nolint
import (
"testing"
@@ -75,8 +75,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) {
return
}
- 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)
+ 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)
}
}
}
diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go
index 1eb6b6ddb4..5953802849 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go
index 957c82e484..c618783c08 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
+package v1_16 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go
index 9d03fbe3c8..2d5d158a09 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
+package v1_16 //nolint
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 a5af2de380..8d3ce7a558 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go
index 2e827f0550..b279967a2c 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
+package v1_16 //nolint
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.Equal(t, attach.RepoID, issue.RepoID)
+ assert.EqualValues(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.Equal(t, attach.RepoID, release.RepoID)
+ assert.EqualValues(t, attach.RepoID, release.RepoID)
}
}
diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go
index 2e4ed8340e..6aa13c50cf 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go
index 4fd42b7bd2..6d7e94141e 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go
index 946e06e399..742397bf32 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
+package v1_16 //nolint
import (
"testing"
diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go
index 6c9caa100f..7cbafc61e5 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go
index 862bdfdcbd..97888b2847 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go
index f35ede138a..115bb313a0 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go
index 4020352f2b..6adcf890af 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
+package v1_16 //nolint
// 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 de57fad8fe..c08c20e51d 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go
index 2c43698b0c..35e0c9f2fb 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go
index d8c8fdcadc..6ba36152f1 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go
index c3241cba57..e8e6b52453 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go
index 4d375307e7..ece03e1305 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
+package v1_16 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go
index 78241bad5b..d6c577083c 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
+package v1_16 //nolint
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 01a9c386eb..581a7d76e9 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
+package v1_16 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go
index 19126ead1f..91208f066c 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go
index fb643324f4..1a11ef096a 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go
index 230838647b..be3100e02a 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
+package v1_16 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go
index 0b94baf8e3..51b7d81e99 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
+package v1_16 //nolint
import (
"encoding/base32"
diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go
index 3b4ac7aa4b..d43fb03106 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
+package v1_16 //nolint
import (
"testing"
@@ -71,5 +71,5 @@ func Test_RemigrateU2FCredentials(t *testing.T) {
return
}
- assert.Equal(t, expected, got)
+ assert.EqualValues(t, expected, got)
}
diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go
index 571a4f55a3..79cb3fa078 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
+package v1_17 //nolint
import (
"testing"
diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go
index 517cf19388..9b72c8610b 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
+package v1_17 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go
index 788792211f..e3f9437121 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
+package v1_17 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go
index b2bbdf7279..bb3f466e52 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
+package v1_17 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go
index 1925324f0f..2268164919 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
+package v1_17 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go
index 748539225d..b338f85417 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
+package v1_17 //nolint
import (
"code.gitea.io/gitea/models/pull"
diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go
index 37aeacb6fc..268f472a42 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
+package v1_17 //nolint
// 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 04626bcbc5..3f970b68a5 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
+package v1_17 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go
index 17d4cd89d4..4c05a9b539 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
+package v1_17 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go
index 6e335cb813..d266029fd9 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
+package v1_17 //nolint
import (
"time"
diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go
index 4ac8c58e1e..904ddc5192 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
+package v1_17 //nolint
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 9e6a67eb18..9e159388bd 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
+package v1_17 //nolint
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go
index a2dc0fae55..9ca54142e2 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
+package v1_17 //nolint
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go
index a5ea537d8a..2ffb94eb1c 100644
--- a/models/migrations/v1_17/v222.go
+++ b/models/migrations/v1_17/v222.go
@@ -1,11 +1,10 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17
+package v1_17 //nolint
import (
"context"
- "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/base"
@@ -30,7 +29,7 @@ func DropOldCredentialIDColumn(x *xorm.Engine) error {
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
- return errors.New("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
+ return fmt.Errorf("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 b2bfb76551..018451ee4c 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
+package v1_17 //nolint
import (
"context"
diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go
index ebcfb45a94..f71a21d1fb 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
+package v1_18 //nolint
import (
"testing"
diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go
index 6dc12020ea..f3d522b91a 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
+package v1_18 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go
index bc6117e38f..b0ac3777fc 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
+package v1_18 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go
index 8ed9761476..f87e24b11d 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
+package v1_18 //nolint
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go
index 3aca686d59..5fe5dcd0c9 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
+package v1_18 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go
index b13f6461bd..3e7a36de15 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
+package v1_18 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go
index bc15e01390..10d9f35097 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
+package v1_18 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go
index 5722dd3557..d489328c00 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
+package v1_18 //nolint
import (
"testing"
diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go
index 078fce7643..ea5b4d02e1 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
+package v1_18 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go
index 25b2f6525d..40db4c2ffe 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
+package v1_18 //nolint
import (
"testing"
diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go
index 87e807be6e..59f42af111 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
+package v1_19 //nolint
import (
"testing"
diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go
index 8ef1e4e743..79e46132f0 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go
index 493dbc6df8..9caf587c1e 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go
index 9eb6d40509..ba4cd8e20b 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
+package v1_19 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go
index 7436ff7483..32c10ab0f4 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
+package v1_19 //nolint
import (
"testing"
@@ -64,7 +64,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) {
assert.Equal(t, e.Meta, got[i].Meta)
if e.HeaderAuthorization == "" {
- assert.Empty(t, got[i].HeaderAuthorizationEncrypted)
+ assert.Equal(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 3475384d6f..728a580807 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go
index 297d90f65a..3715de3920 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go
index 0ed4d97a27..f172a85b1f 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go
index cf30226ccd..b23c765aa5 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go
index de681bfc7a..266e6cea58 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go
index 8f4a65be95..10076f2401 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go
index 7fdbaeb9dc..4505f86299 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/models/db"
diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go
index e35801a057..a617d6fd2f 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go
index e9e759eaaa..4470835214 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
+package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go
index 9c3f372594..55bbfafb2f 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
+package v1_19 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go
index 2fd63a7118..92a1a9f622 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
+package v1_20 //nolint
import (
"testing"
diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go
index 76cdccaca5..977566ad7d 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
+package v1_20 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go
index 4acb11416c..ab58d12880 100644
--- a/models/migrations/v1_20/v245.go
+++ b/models/migrations/v1_20/v245.go
@@ -1,10 +1,11 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20
+package v1_20 //nolint
import (
"context"
+ "fmt"
"code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/setting"
@@ -56,7 +57,7 @@ func RenameWebhookOrgToOwner(x *xorm.Engine) error {
return err
}
sqlType := x.Dialect().SQLType(inferredTable.GetColumn("org_id"))
- if _, err := sess.Exec("ALTER TABLE `webhook` CHANGE org_id owner_id " + sqlType); err != nil {
+ if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", 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 22bf723404..e6340ef079 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
+package v1_20 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go
index 4f82937e18..59fc5c46b5 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go
index 4f2091e4bc..40555210e7 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
+package v1_20 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go
index c6d3a177ca..02951a74d6 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go
index ec45e6e5c3..86388ef0b8 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
+package v1_20 //nolint
import (
"strings"
diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go
index a274c22a73..7743248a3f 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go
index d6aa602753..ab61cd9b8b 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go
index c96454dbf9..96c494bd8d 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go
index 9cdbfb3916..1e26979a5b 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
+package v1_20 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go
index caf198700e..14b70f8f96 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go
index 7b84c1e154..822153b93e 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
+package v1_20 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go
index 9d5f7c07df..6c6ca4c748 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
+package v1_20 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go
index 1d3faffdae..47174ce805 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
+package v1_20 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go
index 9e0dc9b61d..5b8ced4ad7 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
+package v1_20 //nolint
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.SplitSeq(token.Scope, ",") {
+ for _, oldScope := range strings.Split(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 0bf63719e5..5bc9a71391 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
+package v1_20 //nolint
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.Len(t, tokens, len(tests))
+ assert.Equal(t, len(tests), len(tokens))
// 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 536a7ade08..9afdea1677 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
+package v1_21 //nolint
import (
"testing"
diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go
index 8540c58ae8..6ca52c5998 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
+package v1_21 //nolint
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 122b98eb93..4ec1160d0b 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
+package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go
index 6e88e29b9d..23e900572a 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go
index 55c418bde0..2c7cbadf0d 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
+package v1_21 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go
index 7fc0ec6024..e81a17ad6d 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
+package v1_21 //nolint
import (
"context"
- "errors"
+ "fmt"
"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 errors.New("no admin user found")
+ return fmt.Errorf("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 b6892acc27..800eb95f72 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go
index 440549e868..79a5f5e14c 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go
index 394139a17e..bc0e954bdc 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
+package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go
index b677d2383e..332793ff07 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go
index 042040927d..475ec02380 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go
index ab7c5660ba..b9cc84d3ac 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go
index 05e1af1351..098f6499d5 100644
--- a/models/migrations/v1_21/v271.go
+++ b/models/migrations/v1_21/v271.go
@@ -1,8 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21
-
+package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go
index 14c1e0c4b0..a729c49f1b 100644
--- a/models/migrations/v1_21/v272.go
+++ b/models/migrations/v1_21/v272.go
@@ -1,8 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21
-
+package v1_21 //nolint
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go
index e614a56a7d..61c79f4a76 100644
--- a/models/migrations/v1_21/v273.go
+++ b/models/migrations/v1_21/v273.go
@@ -1,8 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21
-
+package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go
index d0b557a151..df5994f159 100644
--- a/models/migrations/v1_21/v274.go
+++ b/models/migrations/v1_21/v274.go
@@ -1,8 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21
-
+package v1_21 //nolint
import (
"time"
diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go
index 2bfe5c72fa..78804a59d6 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go
index 3ab7e22cd0..15177bf040 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
+package v1_21 //nolint
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.ParseGitURL(remoteURL)
+ u, err := giturl.Parse(remoteURL)
if err != nil {
return "", err
}
diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go
index 0c102eddde..12529160b7 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go
index 846f228678..d6a462d1e7 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go
index beb39effe1..2abd1bbe84 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
+package v1_21 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go
index ac8facd6aa..efd8dbaa8c 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
+package v1_22 //nolint
import (
"testing"
diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go
index 2271cb6089..a8ee4a3bf7 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
+package v1_22 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go
index 129ec2cba0..fc1866aa83 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
+package v1_22 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go
index eed64c30f7..baad9e0916 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
+package v1_22 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go
index 0eca031b65..0a45c51245 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
+package v1_22 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go
index 743f860466..e89a7cbfc2 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
+package v1_22 //nolint
import (
"testing"
diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go
index 31b38f6aed..2b95078980 100644
--- a/models/migrations/v1_22/v284.go
+++ b/models/migrations/v1_22/v284.go
@@ -1,8 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22
-
+package v1_22 //nolint
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go
index fed89f670e..a55cc17c04 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
+package v1_22 //nolint
import (
"time"
diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go
index f3ba50dbb6..1fcde33202 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
+package v1_22 //nolint
import (
"errors"
diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go
index b4a50f6fcb..1f213ddb6e 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
+package v1_22 //nolint
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.Equal(t, "sha1", repo.ObjectFormatName)
+ assert.EqualValues(t, "sha1", repo.ObjectFormatName)
repo = new(Repository)
ok, err = x.ID(id).Get(repo)
assert.NoError(t, err)
assert.True(t, ok)
- assert.Equal(t, "sha256", repo.ObjectFormatName)
+ assert.EqualValues(t, "sha256", repo.ObjectFormatName)
}
diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go
index 5fd901f9de..c8b1593286 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
+package v1_22 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v287_test.go b/models/migrations/v1_22/v287_test.go
index 2b42a33c38..9c7b10947d 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
+package v1_22 //nolint
import (
- "strconv"
+ "fmt"
"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, strconv.FormatInt(e.ID, 10), got.Slug)
+ assert.Equal(t, fmt.Sprintf("%d", e.ID), 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 26c850c218..7c93bfcc66 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
+package v1_22 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go
index 78689a4ffa..b9941aadd9 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
+package v1_22 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go
index 0f4d78410c..9c54d4e87c 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
+package v1_22 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go
index 823a644a95..74726fae96 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
+package v1_22 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go
index 440f48ce80..beca556aee 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
+package v1_22 //nolint
// 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 5299b8618f..53cc719294 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
+package v1_22 //nolint
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 2c8f7683a8..cfe4345143 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
+package v1_22 //nolint
import (
"testing"
diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go
index 8776e51a16..20e261fb1b 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
+package v1_22 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go
index 1cf03d6120..a1d702cb77 100644
--- a/models/migrations/v1_22/v294_test.go
+++ b/models/migrations/v1_22/v294_test.go
@@ -1,9 +1,10 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22
+package v1_22 //nolint
import (
+ "slices"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -43,7 +44,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
for _, index := range tables[0].Indexes {
if index.Type == schemas.UniqueType {
found = true
- assert.ElementsMatch(t, index.Cols, []string{"project_id", "issue_id"})
+ slices.Equal(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 319b1a399b..17bdadb4ad 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
+package v1_22 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go
index 75350f9f65..1ecacab95f 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
+package v1_22 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v297.go b/models/migrations/v1_22/v297.go
index 9a4405f266..7d4b506925 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
+package v1_22 //nolint
import (
"code.gitea.io/gitea/models/perm"
diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go
index 7700173a00..b9f3b95ade 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
+package v1_22 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go
index f7b2caed83..b7948bd4dd 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
+package v1_23 //nolint
import (
"testing"
diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go
index 11021d8855..f6db960c3b 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
+package v1_23 //nolint
import "xorm.io/xorm"
@@ -14,9 +14,5 @@ func AddContentVersionToIssueAndComment(x *xorm.Engine) error {
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(Comment), new(Issue))
- return err
+ return x.Sync(new(Comment), new(Issue))
}
diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go
index 13c6489c5e..f1f1cccdbf 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
+package v1_23 //nolint
import "xorm.io/xorm"
@@ -13,9 +13,5 @@ func AddForcePushBranchProtection(x *xorm.Engine) error {
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(ProtectedBranch))
- return err
+ return x.Sync(new(ProtectedBranch))
}
diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go
index ed8e9ef059..b7797f6c6b 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
+package v1_23 //nolint
import "xorm.io/xorm"
@@ -10,9 +10,5 @@ func AddSkipSecondaryAuthColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
type oauth2Application struct {
SkipSecondaryAuthorization bool `xorm:"NOT NULL DEFAULT FALSE"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(oauth2Application))
- return err
+ return x.Sync(new(oauth2Application))
}
diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go
index e4a50b3ec8..d7ea03eb3d 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
+package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
@@ -14,8 +14,5 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error {
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
LogExpired bool `xorm:"index(stopped_log_expired)"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreDropIndices: true,
- }, new(ActionTask))
- return err
+ return x.Sync(new(ActionTask))
}
diff --git a/models/migrations/v1_23/v302_test.go b/models/migrations/v1_23/v302_test.go
deleted file mode 100644
index b008b6fc03..0000000000
--- a/models/migrations/v1_23/v302_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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 dc541a9535..adfe917d3f 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
+package v1_23 //nolint
import (
"xorm.io/xorm"
@@ -19,9 +19,5 @@ func AddCommentMetaDataColumn(x *xorm.Engine) error {
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(Comment))
- return err
+ return x.Sync(new(Comment))
}
diff --git a/models/migrations/v1_23/v304.go b/models/migrations/v1_23/v304.go
index 35d4d4881a..65cffedbd9 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
+package v1_23 //nolint
import "xorm.io/xorm"
@@ -9,8 +9,5 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error {
type Release struct {
Sha1 string `xorm:"INDEX VARCHAR(64)"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreDropIndices: true,
- }, new(Release))
- return err
+ return x.Sync(new(Release))
}
diff --git a/models/migrations/v1_23/v304_test.go b/models/migrations/v1_23/v304_test.go
deleted file mode 100644
index c3dfa5e7e7..0000000000
--- a/models/migrations/v1_23/v304_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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 3762279de1..4d881192b2 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
+package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v306.go b/models/migrations/v1_23/v306.go
index c5c89dbeb8..276b438e95 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
+package v1_23 //nolint
import "xorm.io/xorm"
@@ -9,9 +9,5 @@ func AddBlockAdminMergeOverrideBranchProtection(x *xorm.Engine) error {
type ProtectedBranch struct {
BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(ProtectedBranch))
- return err
+ return x.Sync(new(ProtectedBranch))
}
diff --git a/models/migrations/v1_23/v307.go b/models/migrations/v1_23/v307.go
index 54a69d250b..ef7f5f2c3f 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
+package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v308.go b/models/migrations/v1_23/v308.go
index 695fdfcc2d..1e8a9b0af2 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
+package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v309.go b/models/migrations/v1_23/v309.go
index e629b718a8..5b39398443 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
+package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v310.go b/models/migrations/v1_23/v310.go
index 074b1c54d3..394417f5a0 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
+package v1_23 //nolint
import (
"xorm.io/xorm"
@@ -12,9 +12,5 @@ func AddPriorityToProtectedBranch(x *xorm.Engine) error {
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(ProtectedBranch))
- return err
+ return x.Sync(new(ProtectedBranch))
}
diff --git a/models/migrations/v1_23/v311.go b/models/migrations/v1_23/v311.go
index ef48085c79..0fc1ac8c0e 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
+package v1_23 //nolint
import (
"xorm.io/xorm"
@@ -11,9 +11,6 @@ func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
type Issue struct {
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
}
- _, err := x.SyncWithOptions(xorm.SyncOptions{
- IgnoreConstrains: true,
- IgnoreIndices: true,
- }, new(Issue))
- return err
+
+ return x.Sync(new(Issue))
}
diff --git a/models/migrations/v1_24/v312.go b/models/migrations/v1_24/v312.go
deleted file mode 100644
index 823b0eae40..0000000000
--- a/models/migrations/v1_24/v312.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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
deleted file mode 100644
index 7e6cda6bfd..0000000000
--- a/models/migrations/v1_24/v313.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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
deleted file mode 100644
index 51cb2e34aa..0000000000
--- a/models/migrations/v1_24/v314.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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
deleted file mode 100644
index 52b9b44785..0000000000
--- a/models/migrations/v1_24/v315.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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
deleted file mode 100644
index 14e888f9ee..0000000000
--- a/models/migrations/v1_24/v316.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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
deleted file mode 100644
index a13db2dd27..0000000000
--- a/models/migrations/v1_24/v317.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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
deleted file mode 100644
index 9b4a540960..0000000000
--- a/models/migrations/v1_24/v318.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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
deleted file mode 100644
index 648081f74e..0000000000
--- a/models/migrations/v1_24/v319.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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
deleted file mode 100644
index ebef71939c..0000000000
--- a/models/migrations/v1_24/v320.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 41f0966942..74434a84a1 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
+package v1_6 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go
index 2b11f57c92..586187228b 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
+package v1_6 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go
index 9fad88a1b6..04cef9a170 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
+package v1_6 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go
index e0b7a28537..b5a748aae3 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
+package v1_7 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go
index 376be37a24..f0567e3c9b 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
+package v1_7 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go
index ef11575466..fa7430970c 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
+package v1_7 //nolint
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go
index 81e9307549..d3fbd94deb 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
+package v1_8 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go
index 4fe5ebe635..8b19993924 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
+package v1_8 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go
index e67f464131..8f041c1484 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
+package v1_8 //nolint
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 3f50114d5a..eb3a9ed0f4 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
+package v1_8 //nolint
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go
index 6f9df47a93..cebbbead28 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
+package v1_8 //nolint
import "xorm.io/xorm"
diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go
index 3c2acc6458..a100dc1ef7 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
+package v1_8 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go
index c685d3b86b..26806dd645 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
+package v1_9 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go
index a0cd57f7c5..10e6c45875 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
+package v1_9 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go
index 423915ae57..c7155fe9cf 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
+package v1_9 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go
index 48e1cd5dc4..a23d7c5d6e 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
+package v1_9 //nolint
import (
"fmt"
diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go
index 9464ff0cf6..cf2725d158 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
+package v1_9 //nolint
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go
index 81a4ebf80d..fa01b6e5e3 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
+package v1_9 //nolint
import (
"xorm.io/xorm"
diff --git a/models/organization/org.go b/models/organization/org.go
index 0f3aef146c..725a99356e 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -9,8 +9,11 @@ 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"
@@ -178,6 +181,12 @@ 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
@@ -398,6 +407,33 @@ 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
@@ -568,9 +604,7 @@ func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
return err
}
-// 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) {
+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).
@@ -582,8 +616,7 @@ func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ..
Find(&teams)
}
-// 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) {
+func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
teamIDs := make([]int64, 0, org.NumTeams)
return teamIDs, db.GetEngine(ctx).
Table("team").
@@ -602,3 +635,180 @@ 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 81457191fe..4c4168af1f 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
- IncludeVisibility structs.VisibleType
+ UserID int64
+ IncludePrivate bool
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
@@ -65,10 +65,11 @@ 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.IncludeVisibility == structs.VisibleTypePrivate)))
+ cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
+ }
+ if !opts.IncludePrivate {
+ cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
}
- // public=0, limited=1, private=2
- cond = cond.And(builder.Lte{"`user`.visibility": opts.IncludeVisibility})
return cond
}
@@ -76,16 +77,6 @@ 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) {
@@ -133,7 +124,6 @@ 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 a2a25c6f91..0f0f8a4bcd 100644
--- a/models/organization/org_list_test.go
+++ b/models/organization/org_list_test.go
@@ -10,32 +10,25 @@ 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 TestOrgList(t *testing.T) {
+func TestCountOrganizations(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{IncludeVisibility: structs.VisibleTypePrivate})
+ cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
assert.NoError(t, err)
assert.Equal(t, expected, cnt)
}
-func testFindOrgs(t *testing.T) {
+func TestFindOrgs(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludeVisibility: structs.VisibleTypePrivate,
+ UserID: 4,
+ IncludePrivate: true,
})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
@@ -43,30 +36,33 @@ func testFindOrgs(t *testing.T) {
}
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
+ UserID: 4,
+ IncludePrivate: false,
})
assert.NoError(t, err)
assert.Empty(t, orgs)
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludeVisibility: structs.VisibleTypePrivate,
+ UserID: 4,
+ IncludePrivate: true,
})
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
}
-func testGetUserOrgsList(t *testing.T) {
+func TestGetUserOrgsList(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
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.Equal(t, 2, orgs[0].NumRepos)
+ assert.EqualValues(t, 2, orgs[0].NumRepos)
}
}
-func testLoadOrgListTeams(t *testing.T) {
+func TestLoadOrgListTeams(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
assert.NoError(t, err)
assert.Len(t, orgs, 1)
@@ -75,10 +71,3 @@ 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
new file mode 100644
index 0000000000..f7e59928f4
--- /dev/null
+++ b/models/organization/org_repo.go
@@ -0,0 +1,17 @@
+// 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 234325a8cd..5e99e88689 100644
--- a/models/organization/org_test.go
+++ b/models/organization/org_test.go
@@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestUser_IsOwnedBy(t *testing.T) {
@@ -135,7 +134,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.Equal(t, expected, isOwner)
+ assert.EqualValues(t, expected, isOwner)
}
test(3, 2, true)
test(3, 3, false)
@@ -149,7 +148,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.Equal(t, expected, isMember)
+ assert.EqualValues(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -164,7 +163,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.Equal(t, expected, isMember)
+ assert.EqualValues(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -181,8 +180,9 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
ID: 29,
IsRestricted: true,
})
- // ensure fixtures return restricted user
- require.True(t, restrictedUser.IsRestricted)
+ if !assert.True(t, restrictedUser.IsRestricted) {
+ return // ensure fixtures return restricted user
+ }
testCases := []struct {
name string
@@ -237,7 +237,7 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
memberUIDs = append(memberUIDs, member.UID)
}
slices.Sort(memberUIDs)
- assert.Equal(t, tc.expectedUIDs, memberUIDs)
+ assert.EqualValues(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.Equal(t, []*organization.OrgUser{{
+ assert.EqualValues(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 := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- count, err := env.CountRepos(db.DefaultContext)
+ count, err := env.CountRepos()
assert.NoError(t, err)
- assert.Equal(t, expectedCount, count)
+ assert.EqualValues(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 := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repoIDs, err := env.RepoIDs(db.DefaultContext)
+ repoIDs, err := env.RepoIDs(1, 100)
assert.NoError(t, err)
assert.Equal(t, expectedRepoIDs, repoIDs)
}
@@ -342,13 +342,32 @@ 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 := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
+ env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repos, err := env.MirrorRepos(db.DefaultContext)
+ repos, err := env.MirrorRepos()
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 4d7527c15f..1d3b2fab44 100644
--- a/models/organization/org_user.go
+++ b/models/organization/org_user.go
@@ -36,21 +36,6 @@ 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).
@@ -78,7 +63,7 @@ func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
return false, err
}
for _, t := range teams {
- if t.HasAdminAccess() {
+ if t.AccessMode >= perm.AccessModeAdmin {
return true, nil
}
}
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
index 689544430d..55abb63203 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.GetBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}) == nil {
+ if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
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.Equal(t, expectedNumMembers, org.NumMembers)
+ assert.EqualValues(t, expectedNumMembers, org.NumMembers)
}
setting.Service.DefaultOrgMemberVisible = false
diff --git a/models/organization/org_worktime.go b/models/organization/org_worktime.go
deleted file mode 100644
index 7b57182a8a..0000000000
--- a/models/organization/org_worktime.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// 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 7f3a9b3829..fb7f0c0493 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -77,8 +78,9 @@ type Team struct {
LowerName string
Name string
Description string
- AccessMode perm.AccessMode `xorm:"'authorize'"`
- Members []*user_model.User `xorm:"-"`
+ AccessMode perm.AccessMode `xorm:"'authorize'"`
+ Repos []*repo_model.Repository `xorm:"-"`
+ Members []*user_model.User `xorm:"-"`
NumRepos int
NumMembers int
Units []*TeamUnit `xorm:"-"`
@@ -113,7 +115,7 @@ func (t *Team) LoadUnits(ctx context.Context) (err error) {
// GetUnitNames returns the team units names
func (t *Team) GetUnitNames() (res []string) {
- if t.HasAdminAccess() {
+ if t.AccessMode >= perm.AccessModeAdmin {
return unit.AllUnitKeyNames()
}
@@ -126,7 +128,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.HasAdminAccess() {
+ if t.AccessMode >= perm.AccessModeAdmin {
for _, u := range unit.Units {
m[u.NameKey] = t.AccessMode.ToString()
}
@@ -153,8 +155,15 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool {
return isMember
}
-func (t *Team) HasAdminAccess() bool {
- return t.AccessMode >= perm.AccessModeAdmin
+// LoadRepositories returns paginated repositories in team of organization.
+func (t *Team) LoadRepositories(ctx context.Context) (err error) {
+ if t.Repos != nil {
+ return nil
+ }
+ t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{
+ TeamID: t.ID,
+ })
+ return err
}
// LoadMembers returns paginated members in team of organization.
@@ -242,6 +251,22 @@ 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 0274f9c5ba..4ceb405e31 100644
--- a/models/organization/team_list.go
+++ b/models/organization/team_list.go
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"xorm.io/builder"
@@ -97,11 +98,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, orgID, repoID int64) (teams TeamList, err error) {
+func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) {
return teams, db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
- Where("team.org_id = ?", orgID).
- And("team_repo.repo_id=?", repoID).
+ Where("team.org_id = ?", repo.OwnerID).
+ And("team_repo.repo_id=?", repo.ID).
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}
@@ -133,8 +134,5 @@ 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 b3e266dbc7..c90dfdeda0 100644
--- a/models/organization/team_repo.go
+++ b/models/organization/team_repo.go
@@ -8,6 +8,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"xorm.io/builder"
@@ -31,6 +32,29 @@ 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{
@@ -50,27 +74,26 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
return err
}
-// 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) {
+// 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) {
teams := make([]*Team, 0, 5)
-
- 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).
+ 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).
- And(builder.Or(
- builder.Expr("team.authorize >= ?", mode),
- builder.In("team.id", sub),
- )).
OrderBy("name").
Find(&teams)
-
- return teams, err
+}
+
+// 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).
+ 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).
+ OrderBy("name").
+ Find(&teams)
}
diff --git a/models/organization/team_repo_test.go b/models/organization/team_repo_test.go
index 73a06a93c2..c0d6750df9 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.GetTeamsWithAccessToAnyRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
+ teams, err := organization.GetTeamsWithAccessToRepoUnit(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 b0bf842584..8c34e7a612 100644
--- a/models/organization/team_test.go
+++ b/models/organization/team_test.go
@@ -8,7 +8,6 @@ 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"
@@ -43,12 +42,9 @@ func TestTeam_GetRepositories(t *testing.T) {
test := func(teamID int64) {
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
- 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 {
+ assert.NoError(t, team.LoadRepositories(db.DefaultContext))
+ assert.Len(t, team.Repos, team.NumRepos)
+ for _, repo := range team.Repos {
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repo.ID})
}
}
@@ -77,7 +73,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.Equal(t, orgID, team.OrgID)
+ assert.EqualValues(t, orgID, team.OrgID)
assert.Equal(t, name, team.Name)
}
testSuccess(3, "Owners")
@@ -95,7 +91,7 @@ func TestGetTeamByID(t *testing.T) {
testSuccess := func(teamID int64) {
team, err := organization.GetTeamByID(db.DefaultContext, teamID)
assert.NoError(t, err)
- assert.Equal(t, teamID, team.ID)
+ assert.EqualValues(t, teamID, team.ID)
}
testSuccess(1)
testSuccess(2)
@@ -163,7 +159,7 @@ func TestGetUserOrgTeams(t *testing.T) {
teams, err := organization.GetUserOrgTeams(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
for _, team := range teams {
- assert.Equal(t, orgID, team.OrgID)
+ assert.EqualValues(t, orgID, team.OrgID)
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{TeamID: team.ID, UID: userID})
}
}
diff --git a/modules/packages/container/const.go b/models/packages/container/const.go
similarity index 65%
rename from modules/packages/container/const.go
rename to models/packages/container/const.go
index 6c7c9b46d1..0dfbda051d 100644
--- a/modules/packages/container/const.go
+++ b/models/packages/container/const.go
@@ -4,8 +4,6 @@
package container
const (
- ContentTypeDockerDistributionManifestV2 = "application/vnd.docker.distribution.manifest.v2+json"
-
ManifestFilename = "manifest.json"
UploadVersion = "_upload"
)
diff --git a/models/packages/container/search.go b/models/packages/container/search.go
index 9321d9eb41..5df35117ce 100644
--- a/models/packages/container/search.go
+++ b/models/packages/container/search.go
@@ -25,7 +25,6 @@ type BlobSearchOptions struct {
Digest string
Tag string
IsManifest bool
- OnlyLead bool
Repository string
}
@@ -44,10 +43,7 @@ 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": container_module.ManifestFilename})
- }
- if opts.OnlyLead {
- cond = cond.And(builder.Eq{"package_file.is_lead": true})
+ cond = cond.And(builder.Eq{"package_file.lower_name": ManifestFilename})
}
if opts.Digest != "" {
var propsCond builder.Cond = builder.Eq{
@@ -77,9 +73,11 @@ func GetContainerBlob(ctx context.Context, opts *BlobSearchOptions) (*packages.P
pfds, err := getContainerBlobsLimit(ctx, opts, 1)
if err != nil {
return nil, err
- } else if len(pfds) == 0 {
+ }
+ if len(pfds) != 1 {
return nil, ErrContainerBlobNotExist
}
+
return pfds[0], nil
}
@@ -235,7 +233,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": container_module.UploadVersion,
+ "package_version.lower_version": 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 2d43dc3046..803b73c968 100644
--- a/models/packages/descriptor.go
+++ b/models/packages/descriptor.go
@@ -11,7 +11,6 @@ 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"
@@ -103,26 +102,19 @@ func (pd *PackageDescriptor) CalculateBlobSize() int64 {
// GetPackageDescriptor gets the package description for a version
func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDescriptor, error) {
- 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)
+ p, err := GetPackageByID(ctx, pv.PackageID)
if err != nil {
return nil, err
}
- o, err := cache.GetWithEphemeralCache(ctx, c, "user", p.OwnerID, user_model.GetUserByID)
+ o, err := user_model.GetUserByID(ctx, p.OwnerID)
if err != nil {
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
- }
+ repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID)
+ if err != nil && !repo_model.IsErrRepoNotExist(err) {
+ return nil, err
}
- creator, err := cache.GetWithEphemeralCache(ctx, c, "user", pv.CreatorID, user_model.GetUserByID)
+ creator, err := user_model.GetUserByID(ctx, pv.CreatorID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
creator = user_model.NewGhostUser()
@@ -150,13 +142,9 @@ func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *c
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)
+ pfds, err := GetPackageFileDescriptors(ctx, pfs)
+ if err != nil {
+ return nil, err
}
var metadata any
@@ -206,7 +194,7 @@ func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *c
case TypeVagrant:
metadata = &vagrant.Metadata{}
default:
- panic("unknown package type: " + string(p.Type))
+ panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
}
if metadata != nil {
if err := json.Unmarshal([]byte(pv.MetadataJSON), &metadata); err != nil {
@@ -230,11 +218,7 @@ func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *c
// GetPackageFileDescriptor gets a package file descriptor for a package file
func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFileDescriptor, error) {
- 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)
+ pb, err := GetBlobByID(ctx, pf.BlobID)
if err != nil {
return nil, err
}
@@ -264,13 +248,9 @@ 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 := GetPackageDescriptorWithCache(ctx, pv, c)
+ pd, err := GetPackageDescriptor(ctx, pv)
if err != nil {
return nil, err
}
diff --git a/models/packages/nuget/search.go b/models/packages/nuget/search.go
index a4b23f31d5..7a505ff08f 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.Paginator.GetSkipTake()
+ skip, take := opts.GetSkipTake()
inner = inner.Limit(take, skip)
}
diff --git a/models/packages/package.go b/models/packages/package.go
index 38d1cdcf66..b7464f9140 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("unknown package type: " + string(pt))
+ panic(fmt.Sprintf("unknown package type: %s", 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("unknown package type: " + string(pt))
+ panic(fmt.Sprintf("unknown package type: %s", string(pt)))
}
// Package represents a package
@@ -228,11 +228,6 @@ 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{})
@@ -318,21 +313,6 @@ 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 bf877485d6..270cb32fdf 100644
--- a/models/packages/package_file.go
+++ b/models/packages/package_file.go
@@ -115,11 +115,6 @@ 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 7ddbfd97e9..e0170016cf 100644
--- a/models/packages/package_property.go
+++ b/models/packages/package_property.go
@@ -66,20 +66,6 @@ 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{})
@@ -92,8 +78,8 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
return err
}
-// DeletePropertiesByName deletes properties by name
-func DeletePropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
+// DeletePropertyByName deletes properties by name
+func DeletePropertyByName(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 5672e0efbf..278e8e3a86 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -14,7 +14,6 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
- "xorm.io/xorm"
)
// ErrDuplicatePackageVersion indicates a duplicated package version error
@@ -188,7 +187,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
- Paginator db.Paginator
+ db.Paginator
}
func (opts *PackageSearchOptions) ToConds() builder.Cond {
@@ -280,19 +279,9 @@ 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
-}
-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
+ // Sort by id for stable order with duplicates in the other field
+ e.Asc("package_version.id")
}
// SearchVersions gets all versions of packages matching the search options
@@ -302,7 +291,16 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
- return searchVersionsBySession(sess, opts)
+
+ 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
}
// SearchLatestVersions gets the latest version of every package matching the search options
@@ -320,7 +318,15 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
Join("INNER", "package", "package.id = package_version.package_id").
Where(builder.In("package_version.id", in))
- return searchVersionsBySession(sess, opts)
+ 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
}
// 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 7de43ecd07..0ed116a132 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -15,7 +15,6 @@ 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"
)
@@ -26,8 +25,7 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode 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
+ everyoneAccessMode map[unit.Type]perm_model.AccessMode
}
// IsOwner returns true if current user is the owner of repository.
@@ -41,8 +39,7 @@ 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 "public(anonymous/everyone) access mode".
-// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
+// It doesn't count the "everyone access mode".
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
@@ -52,22 +49,13 @@ func (p *Permission) HasAnyUnitAccess() bool {
return p.AccessMode >= perm_model.AccessModeRead
}
-func (p *Permission) HasAnyUnitPublicAccess() bool {
- for _, v := range p.anonymousAccessMode {
- if v >= perm_model.AccessModeRead {
- return true
- }
- }
+func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
- return false
-}
-
-func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
- return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
+ return p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@@ -85,16 +73,14 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
-// It also considers "public (anonymous/everyone) access mode"
+// It also considers "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 := p.AccessMode
- unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
- unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
+ unitDefaultAccessMode := max(p.AccessMode, 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)
}
@@ -166,7 +152,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
}
func (p *Permission) LogString() string {
- format := ""
+ format += " ]>"
return fmt.Sprintf(format, args...)
}
-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 {
- applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
- }
-
+func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
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
+ 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)
}
- }
- 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)
+ perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
}
}
}
@@ -257,9 +193,11 @@ func finalProcessRepoUnitPermission(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 {
- finalProcessRepoUnitPermission(user, &perm)
+ applyEveryoneRepoPermission(user, &perm)
+ }
+ if log.IsTrace() {
+ log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
}
- log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
}()
if err = repo.LoadUnits(ctx); err != nil {
@@ -268,6 +206,7 @@ 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
@@ -286,8 +225,7 @@ 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 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"
+ // 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
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -305,7 +243,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
- // plain user TODO: this check should be replaced, only need to check collaborator access mode
+ // plain user
perm.AccessMode, err = accessLevel(ctx, user, repo)
if err != nil {
return perm, err
@@ -315,19 +253,6 @@ 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
@@ -337,9 +262,15 @@ 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.HasAdminAccess() {
+ if team.AccessMode >= perm_model.AccessModeAdmin {
perm.AccessMode = perm_model.AccessModeOwner
perm.unitsMode = nil
return perm, nil
@@ -347,12 +278,29 @@ 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 {
- unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
+ 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)
}
- perm.unitsMode[u.Type] = unitAccessMode
}
}
@@ -400,7 +348,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
}
for _, team := range teams {
- if team.HasAdminAccess() {
+ if team.AccessMode >= perm_model.AccessModeAdmin {
return true, nil
}
}
@@ -409,13 +357,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:revive // export stutter
+func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
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:revive // export stutter
+func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
perm, err := GetUserRepoPermission(ctx, repo, user)
if err != nil {
return perm_model.AccessModeNone, err
@@ -523,7 +471,3 @@ 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 c8675b1ded..50070c4368 100644
--- a/models/perm/access/repo_permission_test.go
+++ b/models/perm/access/repo_permission_test.go
@@ -6,16 +6,12 @@ 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) {
@@ -26,21 +22,14 @@ func TestHasAnyUnitAccess(t *testing.T) {
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.False(t, perm.HasAnyUnitAccess())
- assert.False(t, perm.HasAnyUnitAccessOrPublicAccess())
+ assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
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.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())
+ assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
perm = Permission{
AccessMode: perm_model.AccessModeRead,
@@ -54,32 +43,14 @@ func TestHasAnyUnitAccess(t *testing.T) {
assert.True(t, perm.HasAnyUnitAccess())
}
-func TestApplyPublicAccessRepoPermission(t *testing.T) {
+func TestApplyEveryoneRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- 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)
+ applyEveryoneRepoPermission(nil, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -88,7 +59,16 @@ func TestApplyPublicAccessRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
+ 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)
assert.True(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -97,22 +77,20 @@ func TestApplyPublicAccessRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
+ applyEveryoneRepoPermission(&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,
},
}
- finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
+ applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanWrite(unit.TypeWiki))
- assert.Len(t, perm.units, 1)
}
func TestUnitAccessMode(t *testing.T) {
@@ -156,45 +134,3 @@ 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 9b9d874997..5f581b5880 100644
--- a/models/project/column.go
+++ b/models/project/column.go
@@ -147,7 +147,7 @@ func NewColumn(ctx context.Context, column *Column) error {
return err
}
if res.ColumnCount >= maxProjectColumns {
- return errors.New("NewBoard: maximum number of columns reached")
+ return fmt.Errorf("NewBoard: maximum number of columns reached")
}
column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
_, err := db.GetEngine(ctx).Insert(column)
@@ -172,7 +172,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error {
}
if column.Default {
- return errors.New("deleteColumnByID: cannot delete default column")
+ return fmt.Errorf("deleteColumnByID: cannot delete default column")
}
// move all issues to the default column
@@ -336,9 +336,6 @@ 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 6a615090a5..66db23a3e4 100644
--- a/models/project/column_test.go
+++ b/models/project/column_test.go
@@ -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.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)
+ 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)
}
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 := range maxProjectColumns - 3 {
+ for i := 0; i < maxProjectColumns-3; i++ {
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 47d1537ec7..98eed2a213 100644
--- a/models/project/issue.go
+++ b/models/project/issue.go
@@ -5,7 +5,7 @@ package project
import (
"context"
- "errors"
+ "fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/util"
@@ -35,7 +35,7 @@ func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error
func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error {
if c.ProjectID != newColumn.ProjectID {
- return errors.New("columns have to be in the same project")
+ return fmt.Errorf("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 f516466854..78cba8b574 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -129,11 +129,11 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) {
return err
}
-func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint:revive // export stutter
+func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint
return fmt.Sprintf("%s/-/projects/%d", org.HomeLink(), projectID)
}
-func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint:revive // export stutter
+func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint
return fmt.Sprintf("%s/projects/%d", repo.Link(), projectID)
}
@@ -247,10 +247,6 @@ 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
}
@@ -271,7 +267,7 @@ func NewProject(ctx context.Context, p *Project) error {
return util.NewInvalidArgumentErrorf("project type is not valid")
}
- p.Title = util.EllipsisDisplayString(p.Title, 255)
+ p.Title, _ = util.SplitStringAtByteN(p.Title, 255)
return db.WithTx(ctx, func(ctx context.Context) error {
if err := db.Insert(ctx, p); err != nil {
@@ -326,7 +322,7 @@ func UpdateProject(ctx context.Context, p *Project) error {
p.CardType = CardTypeTextOnly
}
- p.Title = util.EllipsisDisplayString(p.Title, 255)
+ p.Title, _ = util.SplitStringAtByteN(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 c2e924e8ae..dd421b4659 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.Equal(t, int64(6), count)
+ assert.EqualValues(t, int64(6), count)
if assert.Len(t, projects, 6) {
for i := range projects {
- assert.Equal(t, tt.wants[i], projects[i].ID)
+ assert.EqualValues(t, tt.wants[i], projects[i].ID)
}
}
}
diff --git a/models/pull/automerge.go b/models/pull/automerge.go
index 3cafacc3a4..f69fcb60d1 100644
--- a/models/pull/automerge.go
+++ b/models/pull/automerge.go
@@ -15,14 +15,13 @@ 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"`
- DeleteBranchAfterMerge bool
- 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"`
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName return database table name for xorm
@@ -50,7 +49,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, deleteBranchAfterMerge bool) error {
+func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string) error {
// Check if we already have a merge scheduled for that pull request
if exists, _, err := GetScheduledMergeByPullID(ctx, pullID); err != nil {
return err
@@ -59,11 +58,10 @@ 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,
- DeleteBranchAfterMerge: deleteBranchAfterMerge,
+ DoerID: doer.ID,
+ PullID: pullID,
+ MergeStyle: style,
+ Message: message,
})
return err
}
diff --git a/models/pull/review_state.go b/models/pull/review_state.go
index 137af00eab..e46a22a49d 100644
--- a/models/pull/review_state.go
+++ b/models/pull/review_state.go
@@ -6,7 +6,6 @@ package pull
import (
"context"
"fmt"
- "maps"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -101,7 +100,9 @@ func mergeFiles(oldFiles, newFiles map[string]ViewedState) map[string]ViewedStat
return oldFiles
}
- maps.Copy(oldFiles, newFiles)
+ for file, viewed := range newFiles {
+ oldFiles[file] = viewed
+ }
return oldFiles
}
diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go
index 407e45fb54..4815643e67 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 hashes with gogit edition.
+ exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
c.commitCache[commitID] = exist
return exist
}
diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go
index ae0fbf0abd..6bd5e91ad1 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, preferLinkType string) string {
- linkType, link := markup.ParseRenderedLink(link, preferLinkType)
- switch linkType {
- case markup.LinkTypeRoot:
- return r.ctx.ResolveLinkRoot(link)
+func (r *RepoComment) ResolveLink(link string, likeType markup.LinkType) (finalLink string) {
+ switch likeType {
+ case markup.LinkTypeApp:
+ finalLink = r.ctx.ResolveLinkApp(link)
default:
- return r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
+ finalLink = r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
}
+ return finalLink
}
var _ markup.RenderHelper = (*RepoComment)(nil)
@@ -44,31 +44,30 @@ 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{opts: util.OptionalArg(opts)}
+ helper := &RepoComment{
+ repoLink: repo.Link(),
+ 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)
- metas = repo.ComposeCommentMetas(ctx)
+ rctx = rctx.WithMetas(repo.ComposeMetas(ctx))
} else {
- // 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"
+ // 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",
+ })
}
- metas["footnoteContextId"] = helper.opts.FootnoteContextID
- rctx = rctx.WithMetas(metas).WithHelper(helper)
+ rctx = rctx.WithHelper(helper)
return rctx
}
diff --git a/models/renderhelper/repo_comment_test.go b/models/renderhelper/repo_comment_test.go
index 3b13bff73c..01e20b9e02 100644
--- a/models/renderhelper/repo_comment_test.go
+++ b/models/renderhelper/repo_comment_test.go
@@ -4,6 +4,7 @@
package renderhelper
import (
+ "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -20,7 +21,7 @@ func TestRepoComment(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -35,7 +36,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(context.Background(), 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
@@ -55,7 +56,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(t.Context(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoComment(context.Background(), 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
@@ -72,11 +73,4 @@ 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 e0375ed280..794828c617 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, preferLinkType string) (finalLink string) {
- linkType, link := markup.ParseRenderedLink(link, preferLinkType)
- switch linkType {
- case markup.LinkTypeRoot:
- finalLink = r.ctx.ResolveLinkRoot(link)
+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)
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,13 +61,15 @@ 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.ComposeRepoFileMetas(ctx))
+ rctx = rctx.WithMetas(repo.ComposeDocumentMetas(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 3b48efba3a..959648b660 100644
--- a/models/renderhelper/repo_file_test.go
+++ b/models/renderhelper/repo_file_test.go
@@ -4,6 +4,7 @@
package renderhelper
import (
+ "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -21,7 +22,7 @@ func TestRepoFile(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoFile(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -36,7 +37,7 @@ func TestRepoFile(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -48,13 +49,13 @@ func TestRepoFile(t *testing.T) {
assert.Equal(t,
`/test
./test
-
-
+
+
`, rendered)
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -62,12 +63,12 @@ func TestRepoFile(t *testing.T) {
`)
assert.NoError(t, err)
assert.Equal(t, `/test
-
+
`, rendered)
})
t.Run("WithCurrentRefPathByTag", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{
+ rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
CurrentRefPath: "/commit/1234",
CurrentTreePath: "my-dir",
}).
@@ -77,7 +78,7 @@ func TestRepoFile(t *testing.T) {