diff --git a/.air.toml b/.air.toml index d506c19426..de97bd8b29 100644 --- a/.air.toml +++ b/.air.toml @@ -2,10 +2,9 @@ root = "." tmp_dir = ".air" [build] -pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs cmd = "make --no-print-directory backend" bin = "gitea" -delay = 2000 +delay = 1000 include_ext = ["go", "tmpl"] include_file = ["main.go"] include_dir = ["cmd", "models", "modules", "options", "routers", "services"] @@ -16,11 +15,8 @@ exclude_dir = [ "modules/avatar/testdata", "modules/git/tests", "modules/migration/file_format_testdata", - "modules/markup/tests/repo/repo1_filepreview", "routers/private/tests", "services/gitdiff/testdata", - "services/migrations/testdata", - "services/webhook/sourcehut/testdata", ] exclude_regex = ["_test.go$", "_gen.go$"] stop_on_error = true diff --git a/.deadcode-out b/.deadcode-out index 62458dd6b6..5e6f81780f 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -22,6 +22,7 @@ package "code.gitea.io/gitea/models/actions" func (ScheduleList).GetRepoIDs func (ScheduleList).LoadTriggerUser func (ScheduleList).LoadRepos + func GetVariableByID package "code.gitea.io/gitea/models/asymkey" func (ErrGPGKeyAccessDenied).Error diff --git a/.dockerignore b/.dockerignore index a1611a1ca5..4c14a94620 100644 --- a/.dockerignore +++ b/.dockerignore @@ -77,6 +77,7 @@ cpu.out /public/assets/css /public/assets/fonts /public/assets/img/avatar +/public/assets/img/webpack /vendor /web_src/fomantic/node_modules /web_src/fomantic/build/* @@ -94,9 +95,6 @@ cpu.out /.air /.go-licenses -# Files and folders that were previously generated -/public/assets/img/webpack - # Snapcraft snap/.snapcraft/ parts/ diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 91019cde84..99ce2e97d6 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -3,7 +3,6 @@ reportUnusedDisableDirectives: true ignorePatterns: - /web_src/js/vendor - - /web_src/fomantic parserOptions: sourceType: module @@ -310,7 +309,7 @@ rules: jquery/no-merge: [2] jquery/no-param: [2] jquery/no-parent: [0] - jquery/no-parents: [2] + jquery/no-parents: [0] jquery/no-parse-html: [2] jquery/no-prop: [2] jquery/no-proxy: [2] @@ -319,8 +318,8 @@ rules: jquery/no-show: [2] jquery/no-size: [2] jquery/no-sizzle: [0] - jquery/no-slide: [2] - jquery/no-submit: [2] + jquery/no-slide: [0] + jquery/no-submit: [0] jquery/no-text: [0] jquery/no-toggle: [2] jquery/no-trigger: [0] @@ -458,7 +457,7 @@ rules: no-jquery/no-other-utils: [2] no-jquery/no-param: [2] no-jquery/no-parent: [0] - no-jquery/no-parents: [2] + no-jquery/no-parents: [0] no-jquery/no-parse-html-literal: [0] no-jquery/no-parse-html: [2] no-jquery/no-parse-json: [2] @@ -537,7 +536,7 @@ rules: no-underscore-dangle: [0] no-unexpected-multiline: [2] no-unmodified-loop-condition: [2] - no-unneeded-ternary: [2] + no-unneeded-ternary: [0] no-unreachable-loop: [2] no-unreachable: [2] no-unsafe-finally: [2] @@ -716,14 +715,12 @@ rules: 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] @@ -740,7 +737,6 @@ rules: 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] diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml index 6181dcf352..a51633ed9b 100644 --- a/.forgejo/workflows/backport.yml +++ b/.forgejo/workflows/backport.yml @@ -45,13 +45,39 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - uses: https://code.forgejo.org/actions/git-backporting@v4.8.0 + - name: Fetch labels + id: fetch-labels + shell: bash + run: | + set -x + echo "Labels retrieved below" + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get -q install -qq -y jq + filtered_labels=$(echo "$LABELS" | jq -c 'map(select(.name | startswith("backport/v")))') + echo "FILTERED_LABELS=${filtered_labels}" >> $GITHUB_ENV + env: + LABELS: ${{ toJSON(github.event.pull_request.labels) }} + - name: Extract targets + id: extract-targets + shell: bash + run: | + set -x + targets="$(echo $FILTERED_LABELS | jq -c '[.[] | .name | sub("backport/"; "")]')" + echo "targets=$(echo $targets)" >> $GITHUB_OUTPUT + + - name: Printing info + shell: bash + run: | + echo "targets: ${{ steps.extract-targets.outputs.targets }}" + echo "target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}" + echo "pull-request: ${{ github.event.pull_request.url }}" + + - uses: https://code.forgejo.org/actions/git-backporting@v4.6.0 with: - target-branch-pattern: "^backport/(?(v.*))$" + target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}/forgejo strategy: ort strategy-option: find-renames - cherry-pick-options: -x + no-squash: true auth: ${{ secrets.BACKPORT_TOKEN }} pull-request: ${{ github.event.pull_request.url }} - auto-no-squash: true - enable-err-notification: true diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 6ce7141004..1a5cc15188 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -1,8 +1,3 @@ -# -# Runs every 2 hours, but Renovate is limited to create new PR before 4am. -# See renovate.json for more settings. -# Automerge is enabled for Renovate PR's but need to be approved before. -# name: renovate on: @@ -10,7 +5,7 @@ on: branches: - 'renovate/**' # self-test updates schedule: - - cron: '0 0/2 * * *' + - cron: '*/30 * * * *' env: RENOVATE_DRY_RUN: ${{ (github.event_name != 'schedule' && github.ref_name != github.event.repository.default_branch) && 'full' || '' }} @@ -22,11 +17,10 @@ jobs: runs-on: docker container: - image: ghcr.io/visualon/renovate:37.330.1 + image: ghcr.io/visualon/renovate:37.272.0 steps: - - name: Load renovate repo cache - uses: https://code.forgejo.org/actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + - uses: https://code.forgejo.org/actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: | .tmp/cache/renovate/repository @@ -34,8 +28,7 @@ jobs: restore-keys: | repo-cache- - - name: Run renovate - run: renovate + - run: renovate env: GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }} LOG_LEVEL: debug @@ -52,7 +45,7 @@ jobs: GIT_COMMITTER_EMAIL: 'forgejo-renovate-action@forgejo.org' - name: Save renovate repo cache - if: always() && env.RENOVATE_DRY_RUN != 'full' + if: always() && env.RENOVATE_DRY_RUN == 'true' uses: https://code.forgejo.org/actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: | diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index cd955c01af..125cb798dc 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -46,7 +46,7 @@ jobs: image: 'docker.io/node:20-bookworm' services: minio: - image: bitnami/minio:2024.3.30 + image: bitnami/minio:2024.2.26 options: >- --hostname gitea.minio env: @@ -136,12 +136,10 @@ jobs: image: 'docker.io/node:20-bookworm' services: minio: - image: bitnami/minio:2024.3.30 + image: bitnami/minio:2024.2.26 env: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 - ldap: - image: docker.io/gitea/test-openldap:latest pgsql: image: 'docker.io/postgres:15' env: @@ -178,7 +176,6 @@ jobs: TAGS: bindata RACE_ENABLED: true USE_REPO_TEST_DIR: 1 - TEST_LDAP: 1 test-sqlite: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker diff --git a/.gitattributes b/.gitattributes index 4e748c071a..51131c7d83 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,5 @@ * text=auto eol=lf *.tmpl linguist-language=go-html-template -*.pb.go linguist-generated /assets/*.json linguist-generated /public/assets/img/svg/*.svg linguist-generated /templates/swagger/v1_json.tmpl linguist-generated diff --git a/.gitea/issue_template/bug-report.yaml b/.gitea/issue_template/bug-report.yaml index 6fab61fcdc..6edbca886f 100644 --- a/.gitea/issue_template/bug-report.yaml +++ b/.gitea/issue_template/bug-report.yaml @@ -87,3 +87,4 @@ body: - SQLite - PostgreSQL - MySQL + - MSSQL diff --git a/.gitignore b/.gitignore index ebbed981e1..b883e079d1 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ cpu.out /public/assets/css /public/assets/fonts /public/assets/licenses.txt +/public/assets/img/webpack /vendor /web_src/fomantic/node_modules /web_src/fomantic/build/* @@ -101,9 +102,6 @@ cpu.out /.go-licenses /.cur-deadcode-out -# Files and folders that were previously generated -/public/assets/img/webpack - # Snapcraft /gitea_a*.txt snap/.snapcraft/ diff --git a/.golangci.yml b/.golangci.yml index 6d835909fc..6d52f99401 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,13 @@ linters: - enable-all: false - disable-all: true - fast: false enable: - bidichk + # - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - depguard - dupl - errcheck - forbidigo - gocritic + # - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. - gofmt - gofumpt - gosimple @@ -18,11 +17,16 @@ linters: - nolintlint - revive - staticcheck + # - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - stylecheck - typecheck - unconvert - unused + # - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - wastedassign + enable-all: false + disable-all: true + fast: false run: timeout: 10m @@ -31,9 +35,6 @@ run: - public - web_src -output: - sort-results: true - linters-settings: stylecheck: checks: ["all", "-ST1005", "-ST1003"] @@ -50,37 +51,27 @@ linters-settings: errorCode: 1 warningCode: 1 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: error-naming - name: exported - - name: identical-branches - name: if-return - name: increment-decrement - - name: indent-error-flow - - name: modifies-value-receiver + - name: var-naming + - name: var-declaration - 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 + - name: indent-error-flow + - name: errorf + - name: duplicated-imports + - name: modifies-value-receiver gofumpt: extra-rules: true depguard: @@ -105,12 +96,8 @@ linters-settings: issues: max-issues-per-linter: 0 max-same-issues: 0 - exclude-dirs: [node_modules, public, web_src] - exclude-case-sensitive: true exclude-rules: - - path: models/db/sql_postgres_with_schema.go - linters: - - nolintlint + # Exclude some linters from running on tests files. - path: _test\.go linters: - gocyclo @@ -128,19 +115,19 @@ issues: - path: cmd linters: - forbidigo - - text: "webhook" - linters: + - linters: - dupl - - text: "`ID' should not be capitalized" - linters: + text: "webhook" + - linters: - gocritic - - text: "swagger" - linters: + text: "`ID' should not be capitalized" + - linters: - unused - deadcode - - text: "argument x is overwritten before first use" - linters: + text: "swagger" + - linters: - staticcheck + text: "argument x is overwritten before first use" - text: "commentFormatting: put a space between `//` and comment text" linters: - gocritic diff --git a/.ignore b/.ignore index 5b96dabd38..5c945ab981 100644 --- a/.ignore +++ b/.ignore @@ -4,8 +4,6 @@ /modules/options/bindata.go /modules/public/bindata.go /modules/templates/bindata.go -/options/gitignore -/options/license -/public/assets /vendor +/public/assets node_modules diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml new file mode 100644 index 0000000000..60cce7dbf7 --- /dev/null +++ b/.stylelintrc.yaml @@ -0,0 +1,223 @@ +plugins: + - stylelint-declaration-strict-value + - stylelint-declaration-block-no-ignored-properties + - "@stylistic/stylelint-plugin" + +ignoreFiles: + - "**/*.go" + +overrides: + - files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console.css", "font_i18n.css"] + rules: + scale-unlimited/declaration-strict-value: null + - files: ["**/chroma/*", "**/codemirror/*"] + rules: + block-no-empty: null + - files: ["**/*.vue"] + customSyntax: postcss-html + +rules: + "@stylistic/at-rule-name-case": null + "@stylistic/at-rule-name-newline-after": null + "@stylistic/at-rule-name-space-after": null + "@stylistic/at-rule-semicolon-newline-after": null + "@stylistic/at-rule-semicolon-space-before": null + "@stylistic/block-closing-brace-empty-line-before": null + "@stylistic/block-closing-brace-newline-after": null + "@stylistic/block-closing-brace-newline-before": null + "@stylistic/block-closing-brace-space-after": null + "@stylistic/block-closing-brace-space-before": null + "@stylistic/block-opening-brace-newline-after": null + "@stylistic/block-opening-brace-newline-before": null + "@stylistic/block-opening-brace-space-after": null + "@stylistic/block-opening-brace-space-before": always + "@stylistic/color-hex-case": lower + "@stylistic/declaration-bang-space-after": never + "@stylistic/declaration-bang-space-before": null + "@stylistic/declaration-block-semicolon-newline-after": null + "@stylistic/declaration-block-semicolon-newline-before": null + "@stylistic/declaration-block-semicolon-space-after": null + "@stylistic/declaration-block-semicolon-space-before": never + "@stylistic/declaration-block-trailing-semicolon": null + "@stylistic/declaration-colon-newline-after": null + "@stylistic/declaration-colon-space-after": null + "@stylistic/declaration-colon-space-before": never + "@stylistic/function-comma-newline-after": null + "@stylistic/function-comma-newline-before": null + "@stylistic/function-comma-space-after": null + "@stylistic/function-comma-space-before": null + "@stylistic/function-max-empty-lines": 0 + "@stylistic/function-parentheses-newline-inside": never-multi-line + "@stylistic/function-parentheses-space-inside": null + "@stylistic/function-whitespace-after": null + "@stylistic/indentation": 2 + "@stylistic/linebreaks": null + "@stylistic/max-empty-lines": 1 + "@stylistic/max-line-length": null + "@stylistic/media-feature-colon-space-after": null + "@stylistic/media-feature-colon-space-before": never + "@stylistic/media-feature-name-case": null + "@stylistic/media-feature-parentheses-space-inside": null + "@stylistic/media-feature-range-operator-space-after": always + "@stylistic/media-feature-range-operator-space-before": always + "@stylistic/media-query-list-comma-newline-after": null + "@stylistic/media-query-list-comma-newline-before": null + "@stylistic/media-query-list-comma-space-after": null + "@stylistic/media-query-list-comma-space-before": null + "@stylistic/named-grid-areas-alignment": null + "@stylistic/no-empty-first-line": null + "@stylistic/no-eol-whitespace": true + "@stylistic/no-extra-semicolons": true + "@stylistic/no-missing-end-of-source-newline": null + "@stylistic/number-leading-zero": null + "@stylistic/number-no-trailing-zeros": null + "@stylistic/property-case": lower + "@stylistic/selector-attribute-brackets-space-inside": null + "@stylistic/selector-attribute-operator-space-after": null + "@stylistic/selector-attribute-operator-space-before": null + "@stylistic/selector-combinator-space-after": null + "@stylistic/selector-combinator-space-before": null + "@stylistic/selector-descendant-combinator-no-non-space": null + "@stylistic/selector-list-comma-newline-after": null + "@stylistic/selector-list-comma-newline-before": null + "@stylistic/selector-list-comma-space-after": always-single-line + "@stylistic/selector-list-comma-space-before": never-single-line + "@stylistic/selector-max-empty-lines": 0 + "@stylistic/selector-pseudo-class-case": lower + "@stylistic/selector-pseudo-class-parentheses-space-inside": never + "@stylistic/selector-pseudo-element-case": lower + "@stylistic/string-quotes": double + "@stylistic/unicode-bom": null + "@stylistic/unit-case": lower + "@stylistic/value-list-comma-newline-after": null + "@stylistic/value-list-comma-newline-before": null + "@stylistic/value-list-comma-space-after": null + "@stylistic/value-list-comma-space-before": null + "@stylistic/value-list-max-empty-lines": 0 + alpha-value-notation: null + annotation-no-unknown: true + at-rule-allowed-list: null + at-rule-disallowed-list: null + at-rule-empty-line-before: null + at-rule-no-unknown: [true, {ignoreAtRules: [tailwind]}] + at-rule-no-vendor-prefix: true + at-rule-property-required-list: null + block-no-empty: true + color-function-notation: null + color-hex-alpha: null + color-hex-length: null + color-named: null + color-no-hex: null + color-no-invalid-hex: true + comment-empty-line-before: null + comment-no-empty: true + comment-pattern: null + comment-whitespace-inside: null + comment-word-disallowed-list: null + custom-media-pattern: null + custom-property-empty-line-before: null + custom-property-no-missing-var-function: true + custom-property-pattern: null + declaration-block-no-duplicate-custom-properties: true + declaration-block-no-duplicate-properties: [true, {ignore: [consecutive-duplicates-with-different-values]}] + declaration-block-no-redundant-longhand-properties: null + declaration-block-no-shorthand-property-overrides: null + declaration-block-single-line-max-declarations: null + declaration-empty-line-before: null + declaration-no-important: null + declaration-property-max-values: null + declaration-property-unit-allowed-list: null + declaration-property-unit-disallowed-list: {line-height: [em]} + declaration-property-value-allowed-list: null + declaration-property-value-disallowed-list: null + declaration-property-value-no-unknown: true + font-family-name-quotes: always-where-recommended + font-family-no-duplicate-names: true + font-family-no-missing-generic-family-keyword: true + font-weight-notation: null + function-allowed-list: null + function-calc-no-unspaced-operator: true + function-disallowed-list: null + function-linear-gradient-no-nonstandard-direction: true + function-name-case: lower + function-no-unknown: true + function-url-no-scheme-relative: null + function-url-quotes: always + function-url-scheme-allowed-list: null + function-url-scheme-disallowed-list: null + hue-degree-notation: null + import-notation: string + keyframe-block-no-duplicate-selectors: true + keyframe-declaration-no-important: true + keyframe-selector-notation: null + keyframes-name-pattern: null + length-zero-no-unit: [true, ignore: [custom-properties], ignoreFunctions: [var]] + max-nesting-depth: null + media-feature-name-allowed-list: null + media-feature-name-disallowed-list: null + media-feature-name-no-unknown: true + media-feature-name-no-vendor-prefix: true + media-feature-name-unit-allowed-list: null + media-feature-name-value-allowed-list: null + media-feature-name-value-no-unknown: true + media-feature-range-notation: null + media-query-no-invalid: true + named-grid-areas-no-invalid: true + no-descending-specificity: null + no-duplicate-at-import-rules: true + no-duplicate-selectors: true + no-empty-source: true + no-invalid-double-slash-comments: true + no-invalid-position-at-import-rule: [true, ignoreAtRules: [tailwind]] + no-irregular-whitespace: true + no-unknown-animations: null + no-unknown-custom-properties: null + number-max-precision: null + plugin/declaration-block-no-ignored-properties: true + property-allowed-list: null + property-disallowed-list: null + property-no-unknown: true + property-no-vendor-prefix: null + rule-empty-line-before: null + rule-selector-property-disallowed-list: null + scale-unlimited/declaration-strict-value: [[/color$/, font-weight], {ignoreValues: /^(inherit|transparent|unset|initial|currentcolor|none)$/, ignoreFunctions: false, disableFix: true, expandShorthand: true}] + selector-anb-no-unmatchable: true + selector-attribute-name-disallowed-list: null + selector-attribute-operator-allowed-list: null + selector-attribute-operator-disallowed-list: null + selector-attribute-quotes: always + selector-class-pattern: null + selector-combinator-allowed-list: null + selector-combinator-disallowed-list: null + selector-disallowed-list: null + selector-id-pattern: null + selector-max-attribute: null + selector-max-class: null + selector-max-combinators: null + selector-max-compound-selectors: null + selector-max-id: null + selector-max-pseudo-class: null + selector-max-specificity: null + selector-max-type: null + selector-max-universal: null + selector-nested-pattern: null + selector-no-qualifying-type: null + selector-no-vendor-prefix: true + selector-not-notation: null + selector-pseudo-class-allowed-list: null + selector-pseudo-class-disallowed-list: null + selector-pseudo-class-no-unknown: true + selector-pseudo-element-allowed-list: null + selector-pseudo-element-colon-notation: double + selector-pseudo-element-disallowed-list: null + selector-pseudo-element-no-unknown: true + selector-type-case: lower + selector-type-no-unknown: [true, {ignore: [custom-elements]}] + shorthand-property-no-redundant-values: true + string-no-newline: true + time-min-milliseconds: null + unit-allowed-list: null + unit-disallowed-list: null + unit-no-unknown: true + value-keyword-case: null + value-no-vendor-prefix: [true, {ignoreValues: [box, inline-box]}] diff --git a/CODEOWNERS b/CODEOWNERS index e30d2c42b4..10e9d52b35 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,8 +16,6 @@ web_src/.* @caesar @crystal @gusted # HTML templates used by the backend. templates/.* @caesar @crystal @gusted -## the issue sidebar was touched by fnetx -templates/repo/issue/view_content/sidebar.* @fnetx # Files related to Go development. @@ -32,9 +30,3 @@ models/.* @dachary @earl-warren @gusted # of how Forgejo comes together. It's tedious to write good integration testing # for code that lives in here. routers/.* @dachary @earl-warren @gusted - -# Let new strings be checked by the translation team. -options/locale/locale_en-US.ini @0ko - -# Personal interest -.*/webhook.* @oliverpool diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 6d1503f034..3f4cba955a 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -100,11 +100,8 @@ ENV GITEA_CUSTOM /var/lib/gitea/custom ENV GITEA_TEMP /tmp/gitea ENV TMPDIR /tmp/gitea -# Legacy config file for backwards compatibility -# TODO: remove on next major version release -ENV GITEA_APP_INI_LEGACY /etc/gitea/app.ini - -ENV GITEA_APP_INI ${GITEA_CUSTOM}/conf/app.ini +#TODO add to docs the ability to define the ini to load (useful to test and revert a config) +ENV GITEA_APP_INI /etc/gitea/app.ini ENV HOME "/var/lib/gitea/git" VOLUME ["/var/lib/gitea", "/etc/gitea"] WORKDIR /var/lib/gitea diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000000..72171f80ed --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,61 @@ +Alexey Makhov (@makhov) +Bo-Yi Wu (@appleboy) +Ethan Koenig (@ethantkoenig) +Kees de Vries (@Bwko) +Kim Carlbäcker (@bkcsoft) +LefsFlare (@LefsFlarey) +Lunny Xiao (@lunny) +Rachid Zarouali (@xinity) +Rémy Boulanouar (@DblK) +Sandro Santilli (@strk) +Thibault Meyer (@0xbaadf00d) +Thomas Boerger (@tboerger) +Patrick G (@geek1011) +Antoine Girard (@sapk) +Lauris Bukšis-Haberkorns (@lafriks) +Jonas Östanbäck (@cez81) +David Schneiderbauer (@daviian) +Peter Žeby (@morlinest) +Matti Ranta (@techknowlogick) +Jonas Franz (@jonasfranz) +Alexey Terentyev (@axifive) +Lanre Adelowo (@adelowo) +Konrad Langenberg (@kolaente) +He-Long Zhang (@BetaCat0) +Andrew Thornton (@zeripath) +John Olheiser (@jolheiser) +Richard Mahn (@richmahn) +Mrsdizzie (@mrsdizzie) +silverwind (@silverwind) +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) +Norwin Roosen (@noerw) +Kyle Dumont (@kdumontnu) +Patrick Schratz (@pat-s) +Janis Estelmann (@KN4CK3R) +Steven Kriegler (@justusbunsi) +Jimmy Praet (@jpraet) +Leon Hofmeister (@delvh) +Wim (@42wim) +Jason Song (@wolfogre) +Yarden Shoham (@yardenshoham) +Yu Tian (@Zettat123) +Eddie Yang <576951401@qq.com> (@yp05327) +Dong Ge (@sillyguodong) +Xinyi Gong (@HesterG) +wxiaoguang (@wxiaoguang) +Gary Moon (@garymoon) +Philip Peterson (@philip-peterson) +Denys Konovalov (@denyskon) +Punit Inani (@puni9869) +CaiCandong <1290147055@qq.com> (@caicandong) +Rui Chen (@chenrui333) +Nanguan Lin (@lng2020) +kerwin612 (@kerwin612) +Gary Wang (@BLumia) diff --git a/Makefile b/Makefile index 6d4e3e0703..86b59950af 100644 --- a/Makefile +++ b/Makefile @@ -26,18 +26,18 @@ DIFF ?= diff --unified XGO_VERSION := go-1.21.x -AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 # renovate: datasource=go -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v2/cmd/editorconfig-checker@2.8.0 # renovate: datasource=go -GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2 # renovate: datasource=go -GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go -MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 # renovate: datasource=go -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.6-0.20240201115257-bcc7c78b7786 # renovate: datasource=go -XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest # renovate: datasource=go -GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go -GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go -ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.27 # renovate: datasource=go -DEADCODE_PACKAGE ?= golang.org/x/tools/internal/cmd/deadcode@v0.14.0 # renovate: datasource=go +AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v2/cmd/editorconfig-checker@2.8.0 +GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 +GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 +MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.6-0.20240201115257-bcc7c78b7786 +XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest +GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 +GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3 +ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.26 +DEADCODE_PACKAGE ?= golang.org/x/tools/internal/cmd/deadcode@v0.14.0 DOCKER_IMAGE ?= gitea/gitea DOCKER_TAG ?= latest @@ -129,7 +129,7 @@ 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 +WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/img/webpack BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) @@ -154,9 +154,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN GO_DIRS := build cmd models modules routers services tests WEB_DIRS := web_src/js web_src/css -ESLINT_FILES := web_src/js tools *.js tests/e2e +ESLINT_FILES := web_src/js tools *.config.js tests/e2e STYLELINT_FILES := web_src/css web_src/js/components/*.vue -SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(wildcard *.go *.js *.md *.yml *.yaml *.toml) +SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github GO_SOURCES := $(wildcard *.go) GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go) @@ -195,6 +195,10 @@ TEST_PGSQL_DBNAME ?= testgitea TEST_PGSQL_USERNAME ?= postgres TEST_PGSQL_PASSWORD ?= postgres TEST_PGSQL_SCHEMA ?= gtestschema +TEST_MSSQL_HOST ?= mssql:1433 +TEST_MSSQL_DBNAME ?= gitea +TEST_MSSQL_USERNAME ?= sa +TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1 .PHONY: all all: build @@ -244,7 +248,7 @@ help: @echo " - show-version-major show major release number only" @echo " - test-frontend test frontend files" @echo " - test-backend test backend files" - @echo " - test-e2e-sqlite[\#name.test.e2e] test end to end using playwright and sqlite" + @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" @@ -304,14 +308,14 @@ clean: e2e*.test \ tests/integration/gitea-integration-* \ tests/integration/indexers-* \ - tests/mysql.ini tests/pgsql.ini man/ \ + tests/mysql.ini tests/pgsql.ini tests/mssql.ini man/ \ tests/e2e/gitea-e2e-*/ \ tests/e2e/indexers-*/ \ tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/ .PHONY: fmt fmt: - @GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' + 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 @# whitespace before it @@ -334,8 +338,8 @@ ifneq "$(TAGS)" "$(shell cat $(TAGS_EVIDENCE) 2>/dev/null)" TAGS_PREREQ := $(TAGS_EVIDENCE) endif -OAPI_CODEGEN_PACKAGE ?= github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 # renovate: datasource=go -KIN_OPENAPI_CODEGEN_PACKAGE ?= github.com/getkin/kin-openapi/cmd/validate@v0.114.0 # renovate: datasource=go +OAPI_CODEGEN_PACKAGE ?= github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 +KIN_OPENAPI_CODEGEN_PACKAGE ?= github.com/getkin/kin-openapi/cmd/validate@v0.114.0 FORGEJO_API_SERVER = routers/api/forgejo/v1/generated.go .PHONY: generate-forgejo-api @@ -607,6 +611,27 @@ test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql .PHONY: test-pgsql-migration test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test +generate-ini-mssql: + sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \ + -e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \ + -e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \ + -e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \ + -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \ + -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ + -e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \ + tests/mssql.ini.tmpl > tests/mssql.ini + +.PHONY: test-mssql +test-mssql: integrations.mssql.test generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test + +.PHONY: test-mssql\#% +test-mssql\#%: integrations.mssql.test generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*) + +.PHONY: test-mssql-migration +test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test + .PHONY: playwright playwright: deps-frontend npx playwright install $(PLAYWRIGHT_FLAGS) @@ -627,10 +652,6 @@ test-e2e-sqlite: playwright e2e.sqlite.test generate-ini-sqlite test-e2e-sqlite\#%: playwright e2e.sqlite.test generate-ini-sqlite GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestE2e/$* -.PHONY: test-e2e-sqlite-firefox\#% -test-e2e-sqlite-firefox\#%: playwright e2e.sqlite.test generate-ini-sqlite - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini PLAYWRIGHT_PROJECT=firefox ./e2e.sqlite.test -test.run TestE2e/$* - .PHONY: test-e2e-mysql test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e @@ -647,6 +668,14 @@ test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql test-e2e-pgsql\#%: playwright e2e.pgsql.test generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test -test.run TestE2e/$* +.PHONY: test-e2e-mssql +test-e2e-mssql: playwright e2e.mssql.test generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test -test.run TestE2e + +.PHONY: test-e2e-mssql\#% +test-e2e-mssql\#%: playwright e2e.mssql.test generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test -test.run TestE2e/$* + .PHONY: test-e2e-debugserver test-e2e-debugserver: e2e.sqlite.test generate-ini-sqlite sed -i s/3003/3000/g tests/sqlite.ini @@ -660,6 +689,10 @@ bench-sqlite: integrations.sqlite.test generate-ini-sqlite bench-mysql: integrations.mysql.test generate-ini-mysql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . +.PHONY: bench-mssql +bench-mssql: integrations.mssql.test generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . + .PHONY: bench-pgsql bench-pgsql: integrations.pgsql.test generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . @@ -678,6 +711,9 @@ integrations.mysql.test: git-check $(GO_SOURCES) integrations.pgsql.test: git-check $(GO_SOURCES) $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test +integrations.mssql.test: git-check $(GO_SOURCES) + $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mssql.test + integrations.sqlite.test: git-check $(GO_SOURCES) $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)' @@ -697,6 +733,11 @@ migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./migrations.pgsql.test +.PHONY: migrations.mssql.test +migrations.mssql.test: $(GO_SOURCES) generate-ini-mssql + $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mssql.test + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./migrations.mssql.test + .PHONY: migrations.sqlite.test migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)' @@ -722,6 +763,17 @@ migrations.individual.pgsql.test: $(GO_SOURCES) migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* + +.PHONY: migrations.individual.mssql.test +migrations.individual.mssql.test: $(GO_SOURCES) generate-ini-mssql + for pkg in $(MIGRATION_PACKAGES); do \ + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' -test.failfast $$pkg || exit 1; \ + done + +.PHONY: migrations.individual.mssql.test\#% +migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* + .PHONY: migrations.individual.sqlite.test migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite for pkg in $(MIGRATION_PACKAGES); do \ @@ -738,6 +790,9 @@ e2e.mysql.test: $(GO_SOURCES) e2e.pgsql.test: $(GO_SOURCES) $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test +e2e.mssql.test: $(GO_SOURCES) + $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mssql.test + e2e.sqlite.test: $(GO_SOURCES) $(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)' @@ -913,9 +968,8 @@ webpack: $(WEBPACK_DEST) $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json @$(MAKE) -s node-check node_modules - @rm -rf $(WEBPACK_DEST_ENTRIES) - @echo "Running webpack..." - @BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack + rm -rf $(WEBPACK_DEST_ENTRIES) + npx webpack @touch $(WEBPACK_DEST) .PHONY: svg diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 6d583f0eff..927d56852b 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,10 +4,6 @@ A minor or major Forgejo release is published every [three months](https://forge A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading from v7.0.0 to v7.0.1 or v7.1.0) does not require manual intervention. But [major releases](https://semver.org/spec/v2.0.0.html#spec-item-8) where the first version number changes (e.g. upgrading from v1.21 to v7.0) contain breaking changes and the release notes explain how to deal with them. -## Upcoming releases (not available yet) - -- [8.0.0](/release-notes/8.0.0/) - ## 7.0.2 This is a bug fix release. See the documentation for more information on the [upgrade procedure](https://forgejo.org/docs/v7.0/admin/upgrade/). diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 62971df47f..d1062d747b 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -294,6 +294,11 @@ "path": "github.com/davecgh/go-spew/spew/LICENSE", "licenseText": "ISC License\n\nCopyright (c) 2012-2016 Dave Collins \u003cdave@davec.name\u003e\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" }, + { + "name": "github.com/denisenkom/go-mssqldb", + "path": "github.com/denisenkom/go-mssqldb/LICENSE.txt", + "licenseText": "Copyright (c) 2012 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/dgryski/go-rendezvous", "path": "github.com/dgryski/go-rendezvous/LICENSE", @@ -499,6 +504,16 @@ "path": "github.com/golang-jwt/jwt/v5/LICENSE", "licenseText": "Copyright (c) 2012 Dave Grijalva\nCopyright (c) 2021 golang-jwt maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n" }, + { + "name": "github.com/golang-sql/civil", + "path": "github.com/golang-sql/civil/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/golang-sql/sqlexp", + "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", @@ -749,6 +764,11 @@ "path": "github.com/minio/minio-go/v7/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/sha256-simd", + "path": "github.com/minio/sha256-simd/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/mitchellh/mapstructure", "path": "github.com/mitchellh/mapstructure/LICENSE", @@ -934,6 +954,11 @@ "path": "github.com/syndtr/goleveldb/leveldb/LICENSE", "licenseText": "Copyright 2012 Suryandaru Triandana \u003csyndtr@gmail.com\u003e\nAll 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 copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\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/tstranex/u2f", + "path": "github.com/tstranex/u2f/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 The Go FIDO U2F Library 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\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/ulikunitz/xz", "path": "github.com/ulikunitz/xz/LICENSE", diff --git a/build/backport-locales.go b/build/backport-locales.go index 3df83ea6d9..d112dd72bd 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -18,8 +18,8 @@ import ( func main() { if len(os.Args) != 2 { - fmt.Println("usage: backport-locales ") - fmt.Println("eg: backport-locales release/v1.19") + println("usage: backport-locales ") + println("eg: backport-locales release/v1.19") os.Exit(1) } diff --git a/build/code-batch-process.go b/build/code-batch-process.go index cc2ab68026..b3ee399420 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -69,7 +69,6 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`)) - co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`)) @@ -204,6 +203,17 @@ Example: `, "file-batch-exec") } +func getGoVersion() string { + goModFile, err := os.ReadFile("go.mod") + if err != nil { + log.Fatalf(`Faild to read "go.mod": %v`, err) + os.Exit(1) + } + goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`) + goModVersionLine := goModVersionRegex.Find(goModFile) + return string(goModVersionLine[3:]) +} + func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) { fileFilter := mainOptions["file-filter"] if fileFilter == "" { @@ -268,8 +278,7 @@ func main() { log.Print("the -d option is not supported by gitea-fmt") } cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w"))) - cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...))) - cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...))) default: log.Fatalf("unknown cmd: %s %v", subCmd, subArgs) } diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 4777a92908..ec92e342d4 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" "os" "text/tabwriter" @@ -92,7 +91,7 @@ func runListAuth(c *cli.Context) error { func runDeleteAuth(c *cli.Context) error { if !c.IsSet("id") { - return errors.New("--id flag is missing") + return fmt.Errorf("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index 8e6239ac33..c151c0af27 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" "net/url" @@ -194,7 +193,7 @@ func runAddOauth(c *cli.Context) error { func runUpdateOauth(c *cli.Context) error { if !c.IsSet("id") { - return errors.New("--id flag is missing") + return fmt.Errorf("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_stmp.go index d724746905..58a6e2ac22 100644 --- a/cmd/admin_auth_stmp.go +++ b/cmd/admin_auth_stmp.go @@ -5,6 +5,7 @@ package cmd import ( "errors" + "fmt" "strings" auth_model "code.gitea.io/gitea/models/auth" @@ -165,7 +166,7 @@ func runAddSMTP(c *cli.Context) error { func runUpdateSMTP(c *cli.Context) error { if !c.IsSet("id") { - return errors.New("--id flag is missing") + return fmt.Errorf("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go index 520557554a..1cbc6f7527 100644 --- a/cmd/admin_user_delete.go +++ b/cmd/admin_user_delete.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" "strings" @@ -43,7 +42,7 @@ var microcmdUserDelete = &cli.Command{ 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") + return fmt.Errorf("You must provide the id, username or email of a user to delete") } ctx, cancel := installSignals() diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index 6c2c10494e..6e78939680 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" auth_model "code.gitea.io/gitea/models/auth" @@ -43,7 +42,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{ func runGenerateAccessToken(c *cli.Context) error { if !c.IsSet("username") { - return errors.New("You must provide a username to generate a token for") + return fmt.Errorf("You must provide a username to generate a token for") } ctx, cancel := installSignals() @@ -69,7 +68,7 @@ func runGenerateAccessToken(c *cli.Context) error { return err } if exist { - return errors.New("access token name has been used already") + return fmt.Errorf("access token name has been used already") } // make sure the scopes are valid diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go index 190b2fc2ef..48c835ad0e 100644 --- a/cmd/doctor_convert.go +++ b/cmd/doctor_convert.go @@ -17,7 +17,7 @@ import ( var cmdDoctorConvert = &cli.Command{ Name: "convert", Usage: "Convert the database", - Description: "A command to convert an existing MySQL database from utf8 to utf8mb4", + Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar", Action: runDoctorConvert, } @@ -35,14 +35,21 @@ func runDoctorConvert(ctx *cli.Context) error { log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) - if setting.Database.Type.IsMySQL() { + switch { + case setting.Database.Type.IsMySQL(): if err := db.ConvertDatabaseTable(); err != nil { log.Fatal("Failed to convert database & table: %v", err) return err } fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4") - } else { - fmt.Println("This command can only be used with a MySQL database") + case setting.Database.Type.IsMSSQL(): + if err := db.ConvertVarcharToNVarchar(); err != nil { + log.Fatal("Failed to convert database from varchar to nvarchar: %v", err) + return err + } + fmt.Println("Converted successfully, please confirm your database's all columns character is NVARCHAR now") + default: + fmt.Println("This command can only be used with a MySQL or MSSQL database") } return nil diff --git a/cmd/dump.go b/cmd/dump.go index 0a18adb27d..3ea92aa112 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -128,7 +128,7 @@ It can be used for backup and capture Forgejo server image to send to maintainer &cli.StringFlag{ Name: "database", Aliases: []string{"d"}, - Usage: "Specify the database SQL syntax: sqlite3, mysql, postgres", + Usage: "Specify the database SQL syntax: sqlite3, mysql, mssql, postgres", }, &cli.BoolFlag{ Name: "skip-repository", diff --git a/cmd/embedded.go b/cmd/embedded.go index 9f03f7be7c..71d483d11c 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error { } if len(matchedAssetFiles) == 0 { - return errors.New("no files matched the given pattern") + return fmt.Errorf("no files matched the given pattern") } else if len(matchedAssetFiles) > 1 { - return errors.New("too many files matched the given pattern, try to be more specific") + return fmt.Errorf("too many files matched the given pattern, try to be more specific") } data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name) @@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error { } if c.NArg() == 0 { - return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)") + return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)") } destdir := "." diff --git a/cmd/hook.go b/cmd/hook.go index ff3059f9df..04df7ce18c 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -482,7 +482,7 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) { fmt.Fprintf(os.Stderr, " %s\n", res.URL) } fmt.Fprintln(os.Stderr, "") - _ = os.Stderr.Sync() + os.Stderr.Sync() } } @@ -782,7 +782,7 @@ func writeFlushPktLine(ctx context.Context, out io.Writer) error { return nil } -// Write an Pkt-Line based on `data` to `out` according to the specification. +// Write an Pkt-Line based on `data` to `out` according to the specifcation. // https://git-scm.com/docs/protocol-common func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error { // Implementations SHOULD NOT send an empty pkt-line ("0004"). diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index 6049b00d5e..2c701f2672 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -4,7 +4,6 @@ package cmd import ( - "errors" "fmt" "os" @@ -250,7 +249,7 @@ func runAddFileLogger(c *cli.Context) error { if c.IsSet("filename") { vals["filename"] = c.String("filename") } else { - return errors.New("filename must be set when creating a file logger") + return fmt.Errorf("filename must be set when creating a file logger") } if c.IsSet("rotate") { vals["rotate"] = c.Bool("rotate") diff --git a/cmd/serv.go b/cmd/serv.go index 0e3006b36b..d5c54f91b8 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -136,7 +136,7 @@ func runServ(c *cli.Context) error { setup(ctx, c.Bool("debug")) if setting.SSH.Disabled { - fmt.Println("Forgejo: SSH has been disabled") + println("Forgejo: SSH has been disabled") return nil } @@ -164,13 +164,13 @@ func runServ(c *cli.Context) error { } switch key.Type { case asymkey_model.KeyTypeDeploy: - fmt.Println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Forgejo does not provide shell access.") + println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Forgejo does not provide shell access.") case asymkey_model.KeyTypePrincipal: - fmt.Println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Forgejo does not provide shell access.") + println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Forgejo does not provide shell access.") default: - fmt.Println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Forgejo does not provide shell access.") + println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Forgejo does not provide shell access.") } - fmt.Println("If this is unexpected, please log in with password and setup Forgejo under another user.") + println("If this is unexpected, please log in with password and setup Forgejo under another user.") return nil } else if c.Bool("debug") { log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND")) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 4eee2cd1ff..72dcaddb60 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -407,8 +407,8 @@ USER = root ;; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning) ;CONN_MAX_LIFETIME = 3s ;; -;; Database maximum number of open connections, default is 100 which is the lowest default from Postgres (MariaDB + MySQL default to 151). Ensure you only increase the value if you configured your database server accordingly. -;MAX_OPEN_CONNS = 100 +;; Database maximum number of open connections, default is 0 meaning no maximum +;MAX_OPEN_CONNS = 0 ;; ;; Whether execute database models migrations automatically ;AUTO_MIGRATION = true @@ -1503,11 +1503,6 @@ LEVEL = Info ;; - manage_ssh_keys: a user cannot configure ssh keys ;; - manage_gpg_keys: a user cannot configure gpg keys ;USER_DISABLED_FEATURES = -;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys`. This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior. -;; - deletion: a user cannot delete their own account -;; - manage_ssh_keys: a user cannot configure ssh keys -;; - manage_gpg_keys: a user cannot configure gpg keys -;;EXTERNAL_USER_DISABLE_FEATURES = ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2331,8 +2326,6 @@ LEVEL = Info ;SHOW_FOOTER_VERSION = true ;; Show template execution time in the footer ;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true -;; Show the "powered by" text in the footer -;SHOW_FOOTER_POWERED_BY = true ;; Generate sitemap. Defaults to `true`. ;ENABLE_SITEMAP = true ;; Enable/Disable RSS/Atom feed diff --git a/docker/rootless/usr/local/bin/docker-entrypoint.sh b/docker/rootless/usr/local/bin/docker-entrypoint.sh index e5fa41cc78..ca509214bf 100755 --- a/docker/rootless/usr/local/bin/docker-entrypoint.sh +++ b/docker/rootless/usr/local/bin/docker-entrypoint.sh @@ -13,10 +13,5 @@ fi if [ $# -gt 0 ]; then exec "$@" else - # TODO: remove on next major version release - # Honour legacy config file if existing - if [ -f ${GITEA_APP_INI_LEGACY} ]; then - GITEA_APP_INI=${GITEA_APP_INI_LEGACY} - fi exec /usr/local/bin/gitea -c ${GITEA_APP_INI} web fi diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh index 09bbeabc63..b480685863 100755 --- a/docker/rootless/usr/local/bin/docker-setup.sh +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -11,18 +11,6 @@ mkdir -p ${GITEA_CUSTOM} && chmod 0700 ${GITEA_CUSTOM} mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP} if [ ! -w ${GITEA_TEMP} ]; then echo "${GITEA_TEMP} is not writable"; exit 1; fi -# TODO: remove on next major version release -# Honour legacy config file if existing, but inform the user -if [ -f ${GITEA_APP_INI_LEGACY} ] && [ ${GITEA_APP_INI} != ${GITEA_APP_INI_LEGACY} ]; then - GITEA_APP_INI_DEFAULT=/var/lib/gitea/custom/conf/app.ini - echo -e \ - "\033[33mWARNING\033[0m: detected configuration file in deprecated default path ${GITEA_APP_INI_LEGACY}." \ - "The new default is ${GITEA_APP_INI_DEFAULT}. To remove this warning, choose one of the options:\n" \ - "* Move ${GITEA_APP_INI_LEGACY} to ${GITEA_APP_INI_DEFAULT} (or to \$GITEA_APP_INI if you want to override this variable)\n" \ - "* Explicitly override GITEA_APP_INI=${GITEA_APP_INI_LEGACY} in the container environment" - GITEA_APP_INI=${GITEA_APP_INI_LEGACY} -fi - #Prepare config file if [ ! -f ${GITEA_APP_INI} ]; then diff --git a/go.mod b/go.mod index 9dc8ae3f5b..5819522902 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( code.gitea.io/gitea-vet v0.2.3 code.gitea.io/sdk/gitea v0.17.1 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.16.1 + connectrpc.com/connect v1.15.0 gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 @@ -15,7 +15,6 @@ require ( gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 - github.com/ProtonMail/go-crypto v1.0.0 github.com/PuerkitoBio/goquery v1.8.1 github.com/alecthomas/chroma/v2 v2.13.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb @@ -23,12 +22,13 @@ require ( github.com/buildkite/terminal-to-html/v3 v3.10.1 github.com/caddyserver/certmagic v0.20.0 github.com/chi-middleware/proxy v1.1.1 + github.com/denisenkom/go-mssqldb v0.12.3 github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 github.com/dustin/go-humanize v1.0.1 - github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 + github.com/editorconfig/editorconfig-core-go/v2 v2.6.1 github.com/emersion/go-imap v1.2.1 github.com/emirpasic/gods v1.18.1 github.com/felixge/fgprof v0.9.4 @@ -65,7 +65,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 - github.com/klauspost/compress v1.17.8 + github.com/klauspost/compress v1.17.7 github.com/klauspost/cpuid/v2 v2.2.6 github.com/lib/pq v1.10.9 github.com/markbates/goth v1.78.0 @@ -74,7 +74,7 @@ require ( github.com/meilisearch/meilisearch-go v0.26.1 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.26 - github.com/minio/minio-go/v7 v7.0.70 + github.com/minio/minio-go/v7 v7.0.69 github.com/msteinert/pam v1.2.0 github.com/nektos/act v0.2.52 github.com/niklasfasching/go-org v1.7.0 @@ -90,13 +90,14 @@ require ( github.com/sassoftware/go-rpmutils v0.2.1-0.20240124161140-277b154961dd github.com/sergi/go-diff v1.3.1 github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.0 + github.com/tstranex/u2f v1.0.0 github.com/ulikunitz/xz v0.5.11 - github.com/urfave/cli/v2 v2.27.2 + github.com/urfave/cli/v2 v2.27.1 github.com/xanzy/go-gitlab v0.96.0 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.0 + github.com/yuin/goldmark v1.6.0 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 golang.org/x/crypto v0.21.0 @@ -130,6 +131,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/RoaringBitmap/roaring v1.7.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect @@ -161,7 +163,7 @@ require ( github.com/couchbase/go-couchbase v0.1.1 // indirect github.com/couchbase/gomemcached v0.3.0 // indirect github.com/couchbase/goutils v0.1.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -191,6 +193,8 @@ require ( github.com/go-webauthn/x v0.1.6 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -222,6 +226,7 @@ require ( github.com/mholt/acmez v1.2.0 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/sha256-simd v1.0.1 // 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 @@ -242,8 +247,8 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.46.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/rhysd/actionlint v1.6.27 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/rhysd/actionlint v1.6.26 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -269,9 +274,9 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect github.com/zeebo/blake3 v0.2.3 // indirect - go.etcd.io/bbolt v1.3.9 // indirect + go.etcd.io/bbolt v1.3.8 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect @@ -279,7 +284,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.16.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -293,7 +298,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.261.1 +replace github.com/nektos/act => gitea.com/gitea/act v0.259.1 replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 diff --git a/go.sum b/go.sum index a42bce3812..b029072e7d 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= 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.16.1 h1:rOdrK/RTI/7TVnn3JsVxt3n028MlTRwmK5Q4heSpjis= -connectrpc.com/connect v1.16.1/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw= +connectrpc.com/connect v1.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo= +connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -52,8 +52,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.261.1 h1:iACWLc/k8wct9fCF2WdYKqn2Hxx6NjW9zbOP79HF4H4= -gitea.com/gitea/act v0.261.1/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= +gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw= +gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w= @@ -70,6 +70,9 @@ github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU= 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 v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -203,8 +206,8 @@ github.com/couchbase/gomemcached v0.3.0 h1:XkMDdP6w7rtvLijDE0/RhcccX+XvAk5cboyBv github.com/couchbase/gomemcached v0.3.0/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/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -233,13 +236,14 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 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/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w= -github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.1 h1:iPCqofzMO41WVbcS/B5Ym7AwHQg9cyQ7Ie/R2XU5L3A= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.1/go.mod h1:VY4oyqUnpULFB3SCRpl24GFDIN1PmfiQIvN/G4ScSNg= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= @@ -372,6 +376,7 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 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= @@ -551,8 +556,8 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -618,8 +623,10 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= 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.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g= -github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= +github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0= +github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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= @@ -633,6 +640,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= @@ -683,6 +691,7 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -706,11 +715,11 @@ github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwy github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= 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.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw= -github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk= +github.com/rhysd/actionlint v1.6.26 h1:zi7jPZf3Ks14gCXYAAL47uBziyFlX7+Xwilqhexct9g= +github.com/rhysd/actionlint v1.6.26/go.mod h1:TIj1DlCgtYLOv5CH9wCK+WJTOr1qAdnFzkGi0IgSCO4= 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= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -783,9 +792,8 @@ 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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/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= @@ -793,6 +801,8 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 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/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= @@ -800,8 +810,8 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= -github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= -github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= @@ -823,8 +833,8 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 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/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= -github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 h1:3seWKGVhGoc66Ht5QlhQsr4xT2caDnFegsnh2NqvENU= github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= @@ -836,8 +846,8 @@ github.com/yuin/goldmark v1.1.32/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.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= -github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= +github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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= @@ -848,8 +858,8 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= @@ -877,6 +887,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 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= @@ -925,8 +936,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -958,6 +969,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1252,6 +1264,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/models/actions/run.go b/models/actions/run.go index 397455e41d..493bd0173c 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -98,10 +98,13 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error { return nil } - 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 := run.Repo.LoadAttributes(ctx); err != nil { return err } @@ -117,19 +120,6 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error { return nil } -func (run *ActionRun) LoadRepo(ctx context.Context) error { - if run == nil || run.Repo != nil { - return nil - } - - repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID) - if err != nil { - return err - } - run.Repo = repo - return nil -} - func (run *ActionRun) Duration() time.Duration { return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration } diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index 6c5d3b3252..6ea6cb9d3b 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -16,9 +16,14 @@ import ( type ActionJobList []*ActionRunJob func (jobs ActionJobList) GetRunIDs() []int64 { - return container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) { - return j.RunID, j.RunID != 0 - }) + ids := make(container.Set[int64], len(jobs)) + for _, j := range jobs { + if j.RunID == 0 { + continue + } + ids.Add(j.RunID) + } + return ids.Values() } func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error { diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 4046c7d369..388bfc4f86 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -19,15 +19,19 @@ type RunList []*ActionRun // GetUserIDs returns a slice of user's id func (runs RunList) GetUserIDs() []int64 { - return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { - return run.TriggerUserID, true - }) + ids := make(container.Set[int64], len(runs)) + for _, run := range runs { + ids.Add(run.TriggerUserID) + } + return ids.Values() } func (runs RunList) GetRepoIDs() []int64 { - return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { - return run.RepoID, true - }) + ids := make(container.Set[int64], len(runs)) + for _, run := range runs { + ids.Add(run.RepoID) + } + return ids.Values() } func (runs RunList) LoadTriggerUser(ctx context.Context) error { diff --git a/models/actions/runner.go b/models/actions/runner.go index 9192925d5a..67f003387b 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -270,7 +270,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { // Only affect action runners were a owner ID is set, as actions runners // could also be created on a repository. return db.GetEngine(ctx).Table("action_runner"). - Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id"). + Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). Where("`action_runner`.owner_id != ?", 0). And(builder.IsNull{"`user`.id"}). Count(new(ActionRunner)) @@ -279,7 +279,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { subQuery := builder.Select("`action_runner`.id"). From("`action_runner`"). - Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id"). + Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). Where(builder.Neq{"`action_runner`.owner_id": 0}). And(builder.IsNull{"`user`.id"}) b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") @@ -289,25 +289,3 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { } return res.RowsAffected() } - -func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { - return db.GetEngine(ctx).Table("action_runner"). - Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id"). - Where("`action_runner`.repo_id != ?", 0). - And(builder.IsNull{"`repository`.id"}). - Count(new(ActionRunner)) -} - -func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { - subQuery := builder.Select("`action_runner`.id"). - From("`action_runner`"). - Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id"). - Where(builder.Neq{"`action_runner`.repo_id": 0}). - And(builder.IsNull{"`repository`.id"}) - b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") - res, err := db.GetEngine(ctx).Exec(b) - if err != nil { - return 0, err - } - return res.RowsAffected() -} diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go index 3ef8ebb254..87f0886b47 100644 --- a/models/actions/runner_list.go +++ b/models/actions/runner_list.go @@ -16,9 +16,14 @@ type RunnerList []*ActionRunner // GetUserIDs returns a slice of user's id func (runners RunnerList) GetUserIDs() []int64 { - return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) { - return runner.OwnerID, runner.OwnerID != 0 - }) + ids := make(container.Set[int64], len(runners)) + for _, runner := range runners { + if runner.OwnerID == 0 { + continue + } + ids.Add(runner.OwnerID) + } + return ids.Values() } func (runners RunnerList) LoadOwners(ctx context.Context) error { @@ -36,9 +41,16 @@ func (runners RunnerList) LoadOwners(ctx context.Context) error { } func (runners RunnerList) getRepoIDs() []int64 { - return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) { - return runner.RepoID, runner.RepoID > 0 - }) + repoIDs := make(container.Set[int64], len(runners)) + for _, runner := range runners { + if runner.RepoID == 0 { + continue + } + if _, ok := repoIDs[runner.RepoID]; !ok { + repoIDs[runner.RepoID] = struct{}{} + } + } + return repoIDs.Values() } func (runners RunnerList) LoadRepos(ctx context.Context) error { diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index 5361b94801..ecd2c1121c 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -18,15 +18,19 @@ type ScheduleList []*ActionSchedule // GetUserIDs returns a slice of user's id func (schedules ScheduleList) GetUserIDs() []int64 { - return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { - return schedule.TriggerUserID, true - }) + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.TriggerUserID) + } + return ids.Values() } func (schedules ScheduleList) GetRepoIDs() []int64 { - return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { - return schedule.RepoID, true - }) + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.RepoID) + } + return ids.Values() } func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go index f7dac72f8b..e9ae268a6e 100644 --- a/models/actions/schedule_spec_list.go +++ b/models/actions/schedule_spec_list.go @@ -16,9 +16,11 @@ import ( type SpecList []*ActionScheduleSpec func (specs SpecList) GetScheduleIDs() []int64 { - return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) { - return spec.ScheduleID, true - }) + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.ScheduleID) + } + return ids.Values() } func (specs SpecList) LoadSchedules(ctx context.Context) error { @@ -44,9 +46,11 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error { } func (specs SpecList) GetRepoIDs() []int64 { - return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) { - return spec.RepoID, true - }) + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.RepoID) + } + return ids.Values() } func (specs SpecList) LoadRepos(ctx context.Context) error { diff --git a/models/actions/task_list.go b/models/actions/task_list.go index 5e17f91441..b07d00b8db 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -16,9 +16,14 @@ import ( type TaskList []*ActionTask func (tasks TaskList) GetJobIDs() []int64 { - return container.FilterSlice(tasks, func(t *ActionTask) (int64, bool) { - return t.JobID, t.JobID != 0 - }) + ids := make(container.Set[int64], len(tasks)) + for _, t := range tasks { + if t.JobID == 0 { + continue + } + ids.Add(t.JobID) + } + return ids.Values() } func (tasks TaskList) LoadJobs(ctx context.Context) error { diff --git a/models/actions/variable.go b/models/actions/variable.go index 8aff844659..14ded60fac 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -6,11 +6,13 @@ package actions import ( "context" "errors" + "fmt" "strings" "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" ) @@ -53,24 +55,24 @@ type FindVariablesOpts struct { db.ListOptions OwnerID int64 RepoID int64 - Name string } func (opts FindVariablesOpts) ToConds() builder.Cond { cond := builder.NewCond() - // Since we now support instance-level variables, - // there is no need to check for null values for `owner_id` and `repo_id` cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) - - if opts.Name != "" { - cond = cond.And(builder.Eq{"name": strings.ToUpper(opts.Name)}) - } return cond } -func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariable, error) { - return db.Find[ActionVariable](ctx, opts) +func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) { + var variable ActionVariable + has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable) + if err != nil { + return nil, err + } else if !has { + return nil, fmt.Errorf("variable with id %d: %w", variableID, util.ErrNotExist) + } + return &variable, nil } func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) { @@ -82,21 +84,9 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) return count != 0, err } -func DeleteVariable(ctx context.Context, id int64) error { - if _, err := db.DeleteByID[ActionVariable](ctx, id); err != nil { - return err - } - return nil -} - func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { variables := map[string]string{} - if err := run.LoadRepo(ctx); err != nil { - log.Error("LoadRepo: %v", err) - return nil, err - } - // Global globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) if err != nil { diff --git a/models/activities/action_list.go b/models/activities/action_list.go index aafb7f8a26..fdf0f35d4f 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -22,9 +22,11 @@ import ( type ActionList []*Action func (actions ActionList) getUserIDs() []int64 { - return container.FilterSlice(actions, func(action *Action) (int64, bool) { - return action.ActUserID, true - }) + userIDs := make(container.Set[int64], len(actions)) + for _, action := range actions { + userIDs.Add(action.ActUserID) + } + return userIDs.Values() } func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) { @@ -48,9 +50,11 @@ func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_mod } func (actions ActionList) getRepoIDs() []int64 { - return container.FilterSlice(actions, func(action *Action) (int64, bool) { - return action.RepoID, true - }) + repoIDs := make(container.Set[int64], len(actions)) + for _, action := range actions { + repoIDs.Add(action.RepoID) + } + return repoIDs.Values() } func (actions ActionList) LoadRepositories(ctx context.Context) error { @@ -76,19 +80,18 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]* userMap = make(map[int64]*user_model.User) } - missingUserIDs := container.FilterSlice(actions, func(action *Action) (int64, bool) { + userSet := make(container.Set[int64], len(actions)) + for _, action := range actions { if action.Repo == nil { - return 0, false + continue + } + if _, ok := userMap[action.Repo.OwnerID]; !ok { + userSet.Add(action.Repo.OwnerID) } - _, alreadyLoaded := userMap[action.Repo.OwnerID] - return action.Repo.OwnerID, !alreadyLoaded - }) - if len(missingUserIDs) == 0 { - return nil } if err := db.GetEngine(ctx). - In("id", missingUserIDs). + In("id", userSet.Values()). Find(&userMap); err != nil { return fmt.Errorf("find user: %w", err) } @@ -132,9 +135,6 @@ func (actions ActionList) LoadComments(ctx context.Context) error { commentIDs = append(commentIDs, action.CommentID) } } - if len(commentIDs) == 0 { - return nil - } commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go index 32d2a5c051..a6140d6f73 100644 --- a/models/activities/notification_list.go +++ b/models/activities/notification_list.go @@ -196,11 +196,15 @@ func (nl NotificationList) LoadAttributes(ctx context.Context) error { return nil } -// getPendingRepoIDs returns all the repositoty ids which haven't been loaded func (nl NotificationList) getPendingRepoIDs() []int64 { - return container.FilterSlice(nl, func(n *Notification) (int64, bool) { - return n.RepoID, n.Repository == nil - }) + ids := make(container.Set[int64], len(nl)) + for _, notification := range nl { + if notification.Repository != nil { + continue + } + ids.Add(notification.RepoID) + } + return ids.Values() } // LoadRepos loads repositories from database diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go index 080075d793..78fcd76d43 100644 --- a/models/activities/user_heatmap.go +++ b/models/activities/user_heatmap.go @@ -39,8 +39,12 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi // Group by 15 minute intervals which will allow the client to accurately shift the timestamp to their timezone. // The interval is based on the fact that there are timezones such as UTC +5:30 and UTC +12:45. groupBy := "created_unix / 900 * 900" - if setting.Database.Type.IsMySQL() { + groupByName := "timestamp" // We need this extra case because mssql doesn't allow grouping by alias + switch { + case setting.Database.Type.IsMySQL(): groupBy = "created_unix DIV 900 * 900" + case setting.Database.Type.IsMSSQL(): + groupByName = groupBy } cond, err := activityQueryCondition(ctx, GetFeedsOptions{ @@ -63,7 +67,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi Table("action"). Where(cond). And("created_unix > ?", timeutil.TimeStampNow()-31536000). - GroupBy("timestamp"). + GroupBy(groupByName). OrderBy("timestamp"). Find(&hdata) } diff --git a/models/asymkey/gpg_key_object_verification.go b/models/asymkey/gpg_key_object_verification.go index e5c31a74a7..67764ffc58 100644 --- a/models/asymkey/gpg_key_object_verification.go +++ b/models/asymkey/gpg_key_object_verification.go @@ -94,6 +94,7 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica Reason: "gpg.error.no_committer_account", } } + } } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 125d64b36f..83d60e3abe 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -8,7 +8,6 @@ import ( "crypto/sha256" "encoding/base32" "encoding/base64" - "errors" "fmt" "net" "net/url" @@ -145,11 +144,6 @@ func (app *OAuth2Application) TableName() string { // ContainsRedirectURI checks if redirectURI is allowed for app func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { - // OAuth2 requires the redirect URI to be an exact match, no dynamic parts are allowed. - // https://stackoverflow.com/questions/55524480/should-dynamic-query-parameters-be-present-in-the-redirection-uri-for-an-oauth2 - // https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3 - // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest - // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1 contains := func(s string) bool { s = strings.TrimSuffix(strings.ToLower(s), "/") for _, u := range app.RedirectURIs { @@ -302,7 +296,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp return nil, err } if app.UID != opts.UserID { - return nil, errors.New("UID mismatch") + return nil, fmt.Errorf("UID mismatch") } builtinApps := BuiltinApplications() if _, builtin := builtinApps[app.ClientID]; builtin { diff --git a/models/auth/source.go b/models/auth/source.go index d03d4975dc..1a3a1b20a6 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -33,7 +33,6 @@ const ( DLDAP // 5 OAuth2 // 6 SSPI // 7 - Remote // 8 ) // String returns the string name of the LoginType @@ -54,7 +53,6 @@ var Names = map[Type]string{ PAM: "PAM", OAuth2: "OAuth2", SSPI: "SPNEGO with SSPI", - Remote: "Remote", } // Config represents login config as far as the db is concerned @@ -183,10 +181,6 @@ func (source *Source) IsSSPI() bool { return source.Type == SSPI } -func (source *Source) IsRemote() bool { - return source.Type == Remote -} - // HasTLS returns true of this source supports TLS. func (source *Source) HasTLS() bool { hasTLSer, ok := source.Cfg.(HasTLSer) diff --git a/models/db/collation.go b/models/db/collation.go index 39d28fa2ff..149e74a85c 100644 --- a/models/db/collation.go +++ b/models/db/collation.go @@ -41,6 +41,20 @@ func findAvailableCollationsMySQL(x *xorm.Engine) (ret container.Set[string], er return ret, nil } +func findAvailableCollationsMSSQL(x *xorm.Engine) (ret container.Set[string], err error) { + var res []struct { + Name string + } + if err = x.SQL("SELECT * FROM sys.fn_helpcollations() WHERE name LIKE '%[_]CS[_]AS%'").Find(&res); err != nil { + return nil, err + } + ret = make(container.Set[string], len(res)) + for _, r := range res { + ret.Add(r.Name) + } + return ret, nil +} + func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) { dbTables, err := x.DBMetas() if err != nil { @@ -70,6 +84,18 @@ func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) { // At the moment, it's safe to ignore the database difference, just trim the prefix and compare. It could be fixed easily if there is any problem in the future. return a == b || strings.TrimPrefix(a, "utf8mb4_") == strings.TrimPrefix(b, "utf8mb4_") } + } else if x.Dialect().URI().DBType == schemas.MSSQL { + if _, err = x.SQL("SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')").Get(&res.DatabaseCollation); err != nil { + return nil, err + } + res.IsCollationCaseSensitive = func(s string) bool { + return strings.HasSuffix(s, "_CS_AS") + } + candidateCollations = []string{"Latin1_General_CS_AS"} + res.AvailableCollation, err = findAvailableCollationsMSSQL(x) + if err != nil { + return nil, err + } } else { return nil, nil } @@ -120,6 +146,10 @@ func alterDatabaseCollation(x *xorm.Engine, collation string) error { if x.Dialect().URI().DBType == schemas.MYSQL { _, err := x.Exec("ALTER DATABASE CHARACTER SET utf8mb4 COLLATE " + collation) return err + } else if x.Dialect().URI().DBType == schemas.MSSQL { + // TODO: MSSQL has many limitations on changing database collation, it could fail in many cases. + _, err := x.Exec("ALTER DATABASE CURRENT COLLATE " + collation) + return err } return errors.New("unsupported database type") } @@ -135,11 +165,12 @@ func preprocessDatabaseCollation(x *xorm.Engine) { } // try to alter database collation to expected if the database is empty, it might fail in some cases (and it isn't necessary to succeed) - // at the moment. + // at the moment, there is no "altering" solution for MSSQL, site admin should manually change the database collation if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 { if err = alterDatabaseCollation(x, r.ExpectedCollation); err != nil { log.Error("Failed to change database collation to %q: %v", r.ExpectedCollation, err) } else { + _, _ = x.Exec("SELECT 1") // after "altering", MSSQL's session becomes invalid, so make a simple query to "refresh" the session if r, err = CheckCollations(x); err != nil { log.Error("Failed to check database collation again after altering: %v", err) // impossible case return diff --git a/models/db/common.go b/models/db/common.go index f3fd3e72ae..ea628bf2a0 100644 --- a/models/db/common.go +++ b/models/db/common.go @@ -47,6 +47,8 @@ func BuilderDialect() string { return builder.SQLITE case setting.Database.Type.IsPostgreSQL(): return builder.POSTGRES + case setting.Database.Type.IsMSSQL(): + return builder.MSSQL default: return "" } diff --git a/models/db/convert.go b/models/db/convert.go index b8b15382e7..8c124471ab 100644 --- a/models/db/convert.go +++ b/models/db/convert.go @@ -47,6 +47,33 @@ func ConvertDatabaseTable() error { return nil } +// ConvertVarcharToNVarchar converts database and tables from varchar to nvarchar if it's mssql +func ConvertVarcharToNVarchar() error { + if x.Dialect().URI().DBType != schemas.MSSQL { + return nil + } + + 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 +JOIN SYS.types ST +ON SC.system_type_id = ST.system_type_id +AND SC.user_type_id = ST.user_type_id +WHERE ST.name ='varchar'`) + if err != nil { + return err + } + for _, row := range res { + if len(row) == 1 { + if _, err = sess.Exec(row[0]); err != nil { + return err + } + } + } + return err +} + // Cell2Int64 converts a xorm.Cell type to int64, // and handles possible irregular cases. func Cell2Int64(val xorm.Cell) int64 { diff --git a/models/db/engine.go b/models/db/engine.go index b7146e87f2..b3a4171e3f 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -22,8 +22,9 @@ import ( "xorm.io/xorm/names" "xorm.io/xorm/schemas" - _ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver - _ "github.com/lib/pq" // Needed for the Postgresql driver + _ "github.com/denisenkom/go-mssqldb" // Needed for the MSSQL driver + _ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver + _ "github.com/lib/pq" // Needed for the Postgresql driver ) var ( @@ -113,8 +114,10 @@ func newXORMEngine() (*xorm.Engine, error) { if err != nil { return nil, err } - if setting.Database.Type.IsMySQL() { + 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 @@ -236,6 +239,7 @@ func NamesToBean(names ...string) ([]any, error) { // Need to map provided names to beans... beanMap := make(map[string]any) for _, bean := range tables { + beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean beanMap[strings.ToLower(x.TableName(bean))] = bean beanMap[strings.ToLower(x.TableName(bean, true))] = bean diff --git a/models/db/index.go b/models/db/index.go index 259ddd6ade..29254b1f07 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -89,6 +89,33 @@ func mysqlGetNextResourceIndex(ctx context.Context, tableName string, groupID in return idx, nil } +func mssqlGetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { + if _, err := GetEngine(ctx).Exec(fmt.Sprintf(` +MERGE INTO %s WITH (HOLDLOCK) AS target +USING (SELECT %d AS group_id) AS source +(group_id) +ON target.group_id = source.group_id +WHEN MATCHED + THEN UPDATE + SET max_index = max_index + 1 +WHEN NOT MATCHED + THEN INSERT (group_id, max_index) + VALUES (%d, 1); +`, tableName, groupID, groupID)); err != nil { + return 0, err + } + + var idx int64 + _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) + if err != nil { + return 0, err + } + if idx == 0 { + return 0, errors.New("cannot get the correct index") + } + return idx, nil +} + // GetNextResourceIndex generates a resource index, it must run in the same transaction where the resource is created func GetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { switch { @@ -96,6 +123,8 @@ func GetNextResourceIndex(ctx context.Context, tableName string, groupID int64) return postgresGetNextResourceIndex(ctx, tableName, groupID) case setting.Database.Type.IsMySQL(): return mysqlGetNextResourceIndex(ctx, tableName, groupID) + case setting.Database.Type.IsMSSQL(): + return mssqlGetNextResourceIndex(ctx, tableName, groupID) } e := GetEngine(ctx) diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 3b6da74149..6dedde4d6c 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -59,13 +59,8 @@ var migrations = []*Migration{ // v9 -> v10 NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser), // v11 -> v12 + // it is a v7.0 migration backport see https://codeberg.org/forgejo/forgejo/pulls/3165#issuecomment-1755941 NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue), - // v12 -> v13 - NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount), - // v13 -> v14 - NewMigration("Add `hide_archive_links` column to `release` table", AddHideArchiveLinksToRelease), - // v14 -> v15 - NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v13.go b/models/forgejo_migrations/v13.go deleted file mode 100644 index 614f68249d..0000000000 --- a/models/forgejo_migrations/v13.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations //nolint:revive - -import "xorm.io/xorm" - -func AddHideArchiveLinksToRelease(x *xorm.Engine) error { - type Release struct { - ID int64 `xorm:"pk autoincr"` - HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` - } - - return x.Sync(&Release{}) -} diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go deleted file mode 100644 index f6dd35ecf0..0000000000 --- a/models/forgejo_migrations/v14.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations //nolint:revive - -import ( - "code.gitea.io/gitea/models/migrations/base" - - "xorm.io/xorm" -) - -func RemoveGiteaSpecificColumnsFromRepositoryAndBadge(x *xorm.Engine) error { - // Make sure the columns exist before dropping them - type Repository struct { - ID int64 - DefaultWikiBranch string - } - if err := x.Sync(&Repository{}); err != nil { - return err - } - - type Badge struct { - ID int64 `xorm:"pk autoincr"` - Slug string - } - err := x.Sync(new(Badge)) - if err != nil { - return err - } - - sess := x.NewSession() - defer sess.Close() - if err := sess.Begin(); err != nil { - return err - } - if err := base.DropTableColumns(sess, "repository", "default_wiki_branch"); err != nil { - return err - } - if err := base.DropTableColumns(sess, "badge", "slug"); err != nil { - return err - } - return sess.Commit() -} diff --git a/models/forgejo_migrations/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go deleted file mode 100644 index 6822524705..0000000000 --- a/models/forgejo_migrations/v1_22/v12.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import "xorm.io/xorm" - -func AddRepoArchiveDownloadCount(x *xorm.Engine) error { - type RepoArchiveDownloadCount struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"index unique(s)"` - ReleaseID int64 `xorm:"index unique(s)"` - Type int `xorm:"unique(s)"` - Count int64 - } - - return x.Sync(&RepoArchiveDownloadCount{}) -} diff --git a/models/git/branch.go b/models/git/branch.go index 7e1c96d769..c905a2d368 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -103,7 +103,7 @@ func (err ErrBranchesEqual) Unwrap() error { type Branch struct { ID int64 RepoID int64 `xorm:"UNIQUE(s)"` - Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mysql, by default), it's case-insensitive at the moment + Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment CommitID string CommitMessage string `xorm:"TEXT"` // it only stores the message summary (the first line) PusherID int64 diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 493611f217..c6fc8ad4b1 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -17,12 +17,15 @@ import ( type BranchList []*Branch func (branches BranchList) LoadDeletedBy(ctx context.Context) error { - ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) { - return branch.DeletedByID, branch.IsDeleted - }) - + ids := container.Set[int64]{} + for _, branch := range branches { + if !branch.IsDeleted { + continue + } + ids.Add(branch.DeletedByID) + } usersMap := make(map[int64]*user_model.User, len(ids)) - if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil { + if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil { return err } for _, branch := range branches { @@ -38,13 +41,14 @@ func (branches BranchList) LoadDeletedBy(ctx context.Context) error { } func (branches BranchList) LoadPusher(ctx context.Context) error { - ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) { - // pusher_id maybe zero because some branches are sync by backend with no pusher - return branch.PusherID, branch.PusherID > 0 - }) - + ids := container.Set[int64]{} + for _, branch := range branches { + if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher + ids.Add(branch.PusherID) + } + } usersMap := make(map[int64]*user_model.User, len(ids)) - if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil { + if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil { return err } for _, branch := range branches { diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 3f2172e50a..45cf6c3c98 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -84,6 +84,34 @@ func mysqlGetCommitStatusIndex(ctx context.Context, repoID int64, sha string) (i return idx, nil } +func mssqlGetCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) { + if _, err := db.GetEngine(ctx).Exec(` +MERGE INTO commit_status_index WITH (HOLDLOCK) AS target +USING (SELECT ? AS repo_id, ? AS sha) AS source +(repo_id, sha) +ON target.repo_id = source.repo_id AND target.sha = source.sha +WHEN MATCHED + THEN UPDATE + SET max_index = max_index + 1 +WHEN NOT MATCHED + THEN INSERT (repo_id, sha, max_index) + VALUES (?, ?, 1); +`, repoID, sha, repoID, sha); err != nil { + return 0, err + } + + var idx int64 + _, err := db.GetEngine(ctx).SQL("SELECT max_index FROM `commit_status_index` WHERE repo_id = ? AND sha = ?", + repoID, sha).Get(&idx) + if err != nil { + return 0, err + } + if idx == 0 { + return 0, errors.New("cannot get the correct index") + } + return idx, nil +} + // GetNextCommitStatusIndex retried 3 times to generate a resource index func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) { _, err := git.NewIDFromString(sha) @@ -96,6 +124,8 @@ func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (in return postgresGetCommitStatusIndex(ctx, repoID, sha) case setting.Database.Type.IsMySQL(): return mysqlGetCommitStatusIndex(ctx, repoID, sha) + case setting.Database.Type.IsMSSQL(): + return mssqlGetCommitStatusIndex(ctx, repoID, sha) } e := db.GetEngine(ctx) @@ -257,27 +287,30 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp } // GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs -func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) { +func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) { type result struct { Index int64 RepoID int64 - SHA string } - results := make([]result, 0, len(repoSHAs)) + results := make([]result, 0, len(repoIDsToLatestCommitSHAs)) getBase := func() *xorm.Session { return db.GetEngine(ctx).Table(&CommitStatus{}) } // Create a disjunction of conditions for each repoID and SHA pair - conds := make([]builder.Cond, 0, len(repoSHAs)) - for _, repoSHA := range repoSHAs { - conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA}) + conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs)) + for repoID, sha := range repoIDsToLatestCommitSHAs { + conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha}) } sess := getBase().Where(builder.Or(conds...)). - Select("max( `index` ) as `index`, repo_id, sha"). - GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc") + Select("max( `index` ) as `index`, repo_id"). + GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc") + + if !listOptions.IsListAll() { + sess = db.SetSessionPagination(sess, &listOptions) + } err := sess.Find(&results) if err != nil { @@ -294,7 +327,7 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map cond := builder.Eq{ "`index`": result.Index, "repo_id": result.RepoID, - "sha": result.SHA, + "sha": repoIDsToLatestCommitSHAs[result.RepoID], } conds = append(conds, cond) } @@ -362,36 +395,16 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) { - type result struct { - Index int64 - SHA string - } - getBase := func() *xorm.Session { - return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID) - } - start := timeutil.TimeStampNow().AddDuration(-before) - results := make([]result, 0, 10) - sess := getBase().And("updated_unix >= ?", start). - Select("max( `index` ) as `index`, sha"). - GroupBy("context_hash, sha").OrderBy("max( `index` ) desc") - - err := sess.Find(&results) - if err != nil { + var contexts []string + if err := db.GetEngine(ctx).Table("commit_status"). + Where("repo_id = ?", repoID).And("updated_unix >= ?", start). + Cols("context").Distinct().Find(&contexts); err != nil { return nil, err } - contexts := make([]string, 0, len(results)) - if len(results) == 0 { - return contexts, nil - } - - conds := make([]builder.Cond, 0, len(results)) - for _, result := range results { - conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA}) - } - return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts) + return contexts, nil } // NewCommitStatusOptions holds options for creating a CommitStatus diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go deleted file mode 100644 index 7603e7aa65..0000000000 --- a/models/git/commit_status_summary.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2024 Gitea. All rights reserved. -// SPDX-License-Identifier: MIT - -package git - -import ( - "context" - - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" - - "xorm.io/builder" -) - -// CommitStatusSummary holds the latest commit Status of a single Commit -type CommitStatusSummary struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` - SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` - State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"` - TargetURL string `xorm:"TEXT"` -} - -func init() { - db.RegisterModel(new(CommitStatusSummary)) -} - -type RepoSHA struct { - RepoID int64 - SHA string -} - -func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA) ([]*CommitStatus, error) { - cond := builder.NewCond() - for _, rs := range repoSHAs { - cond = cond.Or(builder.Eq{"repo_id": rs.RepoID, "sha": rs.SHA}) - } - - var summaries []CommitStatusSummary - if err := db.GetEngine(ctx).Where(cond).Find(&summaries); err != nil { - return nil, err - } - - commitStatuses := make([]*CommitStatus, 0, len(repoSHAs)) - for _, summary := range summaries { - commitStatuses = append(commitStatuses, &CommitStatus{ - RepoID: summary.RepoID, - SHA: summary.SHA, - State: summary.State, - TargetURL: summary.TargetURL, - }) - } - return commitStatuses, nil -} - -func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error { - commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll) - if err != nil { - return err - } - 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() { - _, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state,target_url) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE state=?", - repoID, sha, state.State, state.TargetURL, state.State) - return err - } - - if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha). - Cols("state, target_url"). - Update(&CommitStatusSummary{ - State: state.State, - TargetURL: state.TargetURL, - }); err != nil { - return err - } else if cnt == 0 { - _, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{ - RepoID: repoID, - SHA: sha, - State: state.State, - TargetURL: state.TargetURL, - }) - return err - } - return nil -} diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index 94c8d3776c..2ada8b3724 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -5,11 +5,15 @@ package git_test import ( "testing" + "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" 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/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -183,3 +187,55 @@ func Test_CalcCommitStatus(t *testing.T) { assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses)) } } + +func TestFindRepoRecentCommitStatusContexts(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo2) + assert.NoError(t, err) + defer gitRepo.Close() + + commit, err := gitRepo.GetBranchCommit(repo2.DefaultBranch) + assert.NoError(t, err) + + defer func() { + _, err := db.DeleteByBean(db.DefaultContext, &git_model.CommitStatus{ + RepoID: repo2.ID, + CreatorID: user2.ID, + SHA: commit.ID.String(), + }) + assert.NoError(t, err) + }() + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusFailure, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusSuccess, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + contexts, err := git_model.FindRepoRecentCommitStatusContexts(db.DefaultContext, repo2.ID, time.Hour) + assert.NoError(t, err) + if assert.Len(t, contexts, 1) { + assert.Equal(t, "compliance/lint-backend", contexts[0]) + } +} diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go index 2f65833fe3..261c73032a 100644 --- a/models/git/lfs_lock.go +++ b/models/git/lfs_lock.go @@ -5,7 +5,7 @@ package git import ( "context" - "errors" + "fmt" "strings" "time" @@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor } if !force && u.ID != lock.OwnerID { - return nil, errors.New("user doesn't own lock and force flag is not set") + return nil, fmt.Errorf("user doesn't own lock and force flag is not set") } if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil { diff --git a/models/issues/comment.go b/models/issues/comment.go index e4b5ed12cd..984fb9c9fc 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -673,8 +673,7 @@ func (c *Comment) LoadTime(ctx context.Context) error { return err } -// LoadReactions loads comment reactions -func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository) (err error) { +func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository) (err error) { if c.Reactions != nil { return nil } @@ -692,6 +691,11 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository return nil } +// LoadReactions loads comment reactions +func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository) error { + return c.loadReactions(ctx, repo) +} + func (c *Comment) loadReview(ctx context.Context) (err error) { if c.ReviewID == 0 { return nil @@ -1289,9 +1293,10 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error { return nil } - issueIDs := container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.IssueID, true - }) + issueIDs := make(container.Set[int64]) + for _, comment := range comments { + issueIDs.Add(comment.IssueID) + } ctx, committer, err := db.TxContext(ctx) if err != nil { @@ -1314,7 +1319,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error { } } - for _, issueID := range issueIDs { + for issueID := range issueIDs { if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?", issueID, CommentTypeComment, issueID); err != nil { return err diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 2f6f57e0da..735c9f0893 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -90,7 +90,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, doer *user_mod return pathToLineToComment, nil } -func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) (CommentList, error) { +func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) { var comments CommentList if review == nil { review = &Review{ID: 0} @@ -169,7 +169,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu } // FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number) -func FetchCodeConversation(ctx context.Context, comment *Comment, doer *user_model.User) (CommentList, error) { +func FetchCodeConversation(ctx context.Context, comment *Comment, doer *user_model.User) ([]*Comment, error) { opts := FindCommentsOptions{ Type: CommentTypeCode, IssueID: comment.IssueID, diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 370b5396e0..30a437ea50 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -17,9 +17,11 @@ import ( type CommentList []*Comment func (comments CommentList) getPosterIDs() []int64 { - return container.FilterSlice(comments, func(c *Comment) (int64, bool) { - return c.PosterID, c.PosterID > 0 - }) + posterIDs := make(container.Set[int64], len(comments)) + for _, comment := range comments { + posterIDs.Add(comment.PosterID) + } + return posterIDs.Values() } // LoadPosters loads posters @@ -39,10 +41,20 @@ func (comments CommentList) LoadPosters(ctx context.Context) error { return nil } +func (comments CommentList) getCommentIDs() []int64 { + ids := make([]int64, 0, len(comments)) + for _, comment := range comments { + ids = append(ids, comment.ID) + } + return ids +} + func (comments CommentList) getLabelIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.LabelID, comment.LabelID > 0 - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + ids.Add(comment.LabelID) + } + return ids.Values() } func (comments CommentList) loadLabels(ctx context.Context) error { @@ -86,9 +98,11 @@ func (comments CommentList) loadLabels(ctx context.Context) error { } func (comments CommentList) getMilestoneIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.MilestoneID, comment.MilestoneID > 0 - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + ids.Add(comment.MilestoneID) + } + return ids.Values() } func (comments CommentList) loadMilestones(ctx context.Context) error { @@ -125,9 +139,11 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { } func (comments CommentList) getOldMilestoneIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.OldMilestoneID, comment.OldMilestoneID > 0 - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + ids.Add(comment.OldMilestoneID) + } + return ids.Values() } func (comments CommentList) loadOldMilestones(ctx context.Context) error { @@ -164,9 +180,11 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { } func (comments CommentList) getAssigneeIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.AssigneeID, comment.AssigneeID > 0 - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + ids.Add(comment.AssigneeID) + } + return ids.Values() } func (comments CommentList) loadAssignees(ctx context.Context) error { @@ -217,9 +235,14 @@ func (comments CommentList) loadAssignees(ctx context.Context) error { // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded func (comments CommentList) getIssueIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.IssueID, comment.Issue == nil - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + if comment.Issue != nil { + continue + } + ids.Add(comment.IssueID) + } + return ids.Values() } // Issues returns all the issues of comments @@ -286,12 +309,14 @@ func (comments CommentList) LoadIssues(ctx context.Context) error { } func (comments CommentList) getDependentIssueIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { if comment.DependentIssue != nil { - return 0, false + continue } - return comment.DependentIssueID, comment.DependentIssueID > 0 - }) + ids.Add(comment.DependentIssueID) + } + return ids.Values() } func (comments CommentList) loadDependentIssues(ctx context.Context) error { @@ -344,35 +369,6 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { return nil } -// getAttachmentCommentIDs only return the comment ids which possibly has attachments -func (comments CommentList) getAttachmentCommentIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.ID, comment.Type.HasAttachmentSupport() - }) -} - -// LoadAttachmentsByIssue loads attachments by issue id -func (comments CommentList) LoadAttachmentsByIssue(ctx context.Context) error { - if len(comments) == 0 { - return nil - } - - attachments := make([]*repo_model.Attachment, 0, len(comments)/2) - if err := db.GetEngine(ctx).Where("issue_id=? AND comment_id>0", comments[0].IssueID).Find(&attachments); err != nil { - return err - } - - commentAttachmentsMap := make(map[int64][]*repo_model.Attachment, len(comments)) - for _, attach := range attachments { - commentAttachmentsMap[attach.CommentID] = append(commentAttachmentsMap[attach.CommentID], attach) - } - - for _, comment := range comments { - comment.Attachments = commentAttachmentsMap[comment.ID] - } - return nil -} - // LoadAttachments loads attachments func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { if len(comments) == 0 { @@ -380,15 +376,16 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { } attachments := make(map[int64][]*repo_model.Attachment, len(comments)) - commentsIDs := comments.getAttachmentCommentIDs() + commentsIDs := comments.getCommentIDs() left := len(commentsIDs) for left > 0 { limit := db.DefaultMaxInSize if left < limit { limit = left } - rows, err := db.GetEngine(ctx). - In("comment_id", commentsIDs[:limit]). + rows, err := db.GetEngine(ctx).Table("attachment"). + Join("INNER", "comment", "comment.id = attachment.comment_id"). + In("comment.id", commentsIDs[:limit]). Rows(new(repo_model.Attachment)) if err != nil { return err @@ -416,9 +413,11 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { } func (comments CommentList) getReviewIDs() []int64 { - return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { - return comment.ReviewID, comment.ReviewID > 0 - }) + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + ids.Add(comment.ReviewID) + } + return ids.Values() } func (comments CommentList) loadReviews(ctx context.Context) error { diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 2235f3d3a8..9ccb93bf42 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -74,9 +74,11 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi } func (issues IssueList) getPosterIDs() []int64 { - return container.FilterSlice(issues, func(issue *Issue) (int64, bool) { - return issue.PosterID, true - }) + posterIDs := make(container.Set[int64], len(issues)) + for _, issue := range issues { + posterIDs.Add(issue.PosterID) + } + return posterIDs.Values() } func (issues IssueList) loadPosters(ctx context.Context) error { @@ -191,9 +193,11 @@ func (issues IssueList) loadLabels(ctx context.Context) error { } func (issues IssueList) getMilestoneIDs() []int64 { - return container.FilterSlice(issues, func(issue *Issue) (int64, bool) { - return issue.MilestoneID, true - }) + ids := make(container.Set[int64], len(issues)) + for _, issue := range issues { + ids.Add(issue.MilestoneID) + } + return ids.Values() } func (issues IssueList) loadMilestones(ctx context.Context) error { @@ -387,8 +391,9 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) { if left < limit { limit = left } - rows, err := db.GetEngine(ctx). - In("issue_id", issuesIDs[:limit]). + rows, err := db.GetEngine(ctx).Table("attachment"). + Join("INNER", "issue", "issue.id = attachment.issue_id"). + In("issue.id", issuesIDs[:limit]). Rows(new(repo_model.Attachment)) if err != nil { return err @@ -607,23 +612,3 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev return approvalCountMap, nil } - -func (issues IssueList) LoadIsRead(ctx context.Context, userID int64) error { - issueIDs := issues.getIssueIDs() - issueUsers := make([]*IssueUser, 0, len(issueIDs)) - if err := db.GetEngine(ctx).Where("uid =?", userID). - In("issue_id", issueIDs). - Find(&issueUsers); err != nil { - return err - } - - for _, issueUser := range issueUsers { - for _, issue := range issues { - if issue.ID == issueUser.IssueID { - issue.IsRead = issueUser.IsRead - } - } - } - - return nil -} diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go index 10ba38a64b..9069e1012d 100644 --- a/models/issues/issue_list_test.go +++ b/models/issues/issue_list_test.go @@ -72,9 +72,4 @@ func TestIssueList_LoadAttributes(t *testing.T) { assert.Nil(t, issue.Project) } } - - assert.NoError(t, issueList.LoadIsRead(db.DefaultContext, 1)) - for _, issue := range issueList { - assert.Equal(t, issue.ID == 1, issue.IsRead, "unexpected is_read value for issue[%d]", issue.ID) - } } diff --git a/models/issues/reaction.go b/models/issues/reaction.go index eb7faefc79..d5448636fe 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -305,12 +305,14 @@ func (list ReactionList) GroupByType() map[string]ReactionList { } func (list ReactionList) getUserIDs() []int64 { - return container.FilterSlice(list, func(reaction *Reaction) (int64, bool) { + userIDs := make(container.Set[int64], len(list)) + for _, reaction := range list { if reaction.OriginalAuthor != "" { - return 0, false + continue } - return reaction.UserID, true - }) + userIDs.Add(reaction.UserID) + } + return userIDs.Values() } func valuesUser(m map[int64]*user_model.User) []*user_model.User { diff --git a/models/issues/review.go b/models/issues/review.go index 3c6934b060..92764db4d1 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -345,9 +345,11 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error return nil, err } } + } else if opts.ReviewerTeam != nil { review.Type = ReviewTypeRequest review.ReviewerTeamID = opts.ReviewerTeam.ID + } else { return nil, fmt.Errorf("provide either reviewer or reviewer team") } diff --git a/models/issues/review_list.go b/models/issues/review_list.go index 7b8c3d319c..ec6cb07988 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -38,11 +38,12 @@ func (reviews ReviewList) LoadReviewers(ctx context.Context) error { } func (reviews ReviewList) LoadIssues(ctx context.Context) error { - issueIDs := container.FilterSlice(reviews, func(review *Review) (int64, bool) { - return review.IssueID, true - }) + issueIDs := container.Set[int64]{} + for i := 0; i < len(reviews); i++ { + issueIDs.Add(reviews[i].IssueID) + } - issues, err := GetIssuesByIDs(ctx, issueIDs) + issues, err := GetIssuesByIDs(ctx, issueIDs.Values()) if err != nil { return err } diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index e584793385..51351cc7d3 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -89,6 +89,13 @@ func RecreateTable(sess *xorm.Session, bean any) error { hasID = hasID || (column.IsPrimaryKey && column.IsAutoIncrement) } + if hasID && setting.Database.Type.IsMSSQL() { + if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` ON", tempTableName)); err != nil { + log.Error("Unable to set identity insert for table %s. Error: %v", tempTableName, err) + return err + } + } + sqlStringBuilder := &strings.Builder{} _, _ = sqlStringBuilder.WriteString("INSERT INTO `") _, _ = sqlStringBuilder.WriteString(tempTableName) @@ -136,6 +143,13 @@ func RecreateTable(sess *xorm.Session, bean any) error { return err } + if hasID && setting.Database.Type.IsMSSQL() { + if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` OFF", tempTableName)); err != nil { + log.Error("Unable to switch off identity insert for table %s. Error: %v", tempTableName, err) + return err + } + } + switch { case setting.Database.Type.IsSQLite3(): // SQLite will drop all the constraints on the old table @@ -214,6 +228,7 @@ func RecreateTable(sess *xorm.Session, bean any) error { return err } sequenceMap[sequence] = sequenceData + } // CASCADE causes postgres to drop all the constraints on the old table @@ -278,6 +293,20 @@ func RecreateTable(sess *xorm.Session, bean any) error { return err } } + + } + + case setting.Database.Type.IsMSSQL(): + // MSSQL will drop all the constraints on the old table + if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { + log.Error("Unable to drop old table %s. Error: %v", tableName, err) + return err + } + + // MSSQL sp_rename will move all the constraints from the temporary table to the new table + if _, err := sess.Exec(fmt.Sprintf("sp_rename `%s`,`%s`", tempTableName, tableName)); err != nil { + log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) + return err } default: @@ -415,6 +444,40 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) } + case setting.Database.Type.IsMSSQL(): + cols := "" + for _, col := range columnNames { + if cols != "" { + cols += ", " + } + cols += "`" + strings.ToLower(col) + "`" + } + sql := fmt.Sprintf("SELECT Name FROM sys.default_constraints WHERE parent_object_id = OBJECT_ID('%[1]s') AND parent_column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", + tableName, strings.ReplaceAll(cols, "`", "'")) + constraints := make([]string, 0) + if err := sess.SQL(sql).Find(&constraints); err != nil { + return fmt.Errorf("Find constraints: %v", err) + } + for _, constraint := range constraints { + if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil { + return fmt.Errorf("Drop table `%s` default constraint `%s`: %v", tableName, constraint, err) + } + } + sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", + tableName, strings.ReplaceAll(cols, "`", "'")) + constraints = make([]string, 0) + if err := sess.SQL(sql).Find(&constraints); err != nil { + return fmt.Errorf("Find constraints: %v", err) + } + for _, constraint := range constraints { + if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil { + return fmt.Errorf("Drop index `%[2]s` on `%[1]s`: %v", tableName, constraint, err) + } + } + + if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil { + return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) + } default: log.Fatal("Unrecognized DB") } @@ -426,6 +489,21 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin func ModifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error { var indexes map[string]*schemas.Index var err error + // MSSQL have to remove index at first, otherwise alter column will fail + // ref. https://sqlzealots.com/2018/05/09/error-message-the-index-is-dependent-on-column-alter-table-alter-column-failed-because-one-or-more-objects-access-this-column/ + if x.Dialect().URI().DBType == schemas.MSSQL { + indexes, err = x.Dialect().GetIndexes(x.DB(), context.Background(), tableName) + if err != nil { + return err + } + + for _, index := range indexes { + _, err = x.Exec(x.Dialect().DropIndexSQL(tableName, index)) + if err != nil { + return err + } + } + } defer func() { for _, index := range indexes { @@ -534,6 +612,21 @@ func deleteDB() error { } return nil } + case setting.Database.Type.IsMSSQL(): + host, port := setting.ParseMSSQLHostPort(setting.Database.Host) + db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, "master", setting.Database.User, setting.Database.Passwd)) + if err != nil { + return err + } + defer db.Close() + + if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS [%s]", setting.Database.Name)); err != nil { + return err + } + if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE [%s]", setting.Database.Name)); err != nil { + return err + } } return nil diff --git a/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml b/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml deleted file mode 100644 index 6feaeb39f0..0000000000 --- a/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml +++ /dev/null @@ -1,9 +0,0 @@ -- - id: 1 - project_id: 1 - issue_id: 1 - -- - id: 2 - project_id: 1 - issue_id: 1 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 5d80f9fd10..df8ffc3308 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -574,21 +574,6 @@ var migrations = []Migration{ NewMigration("Ensure every project has exactly one default column - No Op", noopMigration), // v293 -> v294 NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency), - - // Gitea 1.22.0-rc0 ends at 294 - - // v294 -> v295 - NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue), - // v295 -> v296 - NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary), - // v296 -> v297 - NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2), - // v297 -> v298 - NewMigration("Add everyone_access_mode for repo_unit", noopMigration), - // v298 -> v299 - NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable), - - // Gitea 1.22.0-rc1 ends at 299 } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go index fce9be847e..81afa1331d 100644 --- a/models/migrations/v1_11/v110.go +++ b/models/migrations/v1_11/v110.go @@ -16,6 +16,9 @@ func ChangeReviewContentToText(x *xorm.Engine) error { case schemas.ORACLE: _, err := x.Exec("ALTER TABLE review MODIFY content TEXT") return err + case schemas.MSSQL: + _, err := x.Exec("ALTER TABLE review ALTER COLUMN content TEXT") + return err case schemas.POSTGRES: _, err := x.Exec("ALTER TABLE review ALTER COLUMN content TYPE TEXT") return err diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index cc3dc0d545..c18d7adae1 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -263,6 +263,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { for _, u := range units { var found bool for _, team := range teams { + var teamU []*TeamUnit var unitEnabled bool err = sess.Where("team_id = ?", team.ID).Find(&teamU) @@ -331,6 +332,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { } if !protectedBranch.EnableApprovalsWhitelist { + perm, err := getUserRepoPermission(sess, baseRepo, reviewer) if err != nil { return false, err diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index 5b6576951d..279aa7df87 100644 --- a/models/migrations/v1_12/v139.go +++ b/models/migrations/v1_12/v139.go @@ -12,9 +12,12 @@ import ( func PrependRefsHeadsToIssueRefs(x *xorm.Engine) error { var query string - if setting.Database.Type.IsMySQL() { + switch { + case setting.Database.Type.IsMSSQL(): + query = "UPDATE `issue` SET `ref` = 'refs/heads/' + `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'" + case setting.Database.Type.IsMySQL(): query = "UPDATE `issue` SET `ref` = CONCAT('refs/heads/', `ref`) WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%';" - } else { + default: query = "UPDATE `issue` SET `ref` = 'refs/heads/' || `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'" } diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index 5b38f1cd80..8acb29bf33 100644 --- a/models/migrations/v1_13/v145.go +++ b/models/migrations/v1_13/v145.go @@ -45,6 +45,32 @@ func IncreaseLanguageField(x *xorm.Engine) error { if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat MODIFY COLUMN language %s", sqlType)); err != nil { return err } + case setting.Database.Type.IsMSSQL(): + // Yet again MSSQL just has to be awkward. + // Here we have to drop the constraints first and then rebuild them + constraints := make([]string, 0) + if err := sess.SQL(`SELECT i.name AS Name + FROM sys.indexes i INNER JOIN sys.index_columns ic + ON i.index_id = ic.index_id AND i.object_id = ic.object_id + INNER JOIN sys.tables AS t + ON t.object_id = i.object_id + INNER JOIN sys.columns c + ON t.object_id = c.object_id AND ic.column_id = c.column_id + WHERE t.name = 'language_stat' AND c.name = 'language'`).Find(&constraints); err != nil { + return fmt.Errorf("Find constraints: %w", err) + } + for _, constraint := range constraints { + if _, err := sess.Exec(fmt.Sprintf("DROP INDEX [%s] ON `language_stat`", constraint)); err != nil { + return fmt.Errorf("Drop table `language_stat` constraint `%s`: %w", constraint, err) + } + } + if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language %s", sqlType)); err != nil { + return err + } + // Finally restore the constraint + if err := sess.CreateUniques(new(LanguageStat)); err != nil { + return err + } case setting.Database.Type.IsPostgreSQL(): if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language TYPE %s", sqlType)); err != nil { return err diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index ea4a8eae31..25af1d03ec 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -23,6 +23,36 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error { case setting.Database.Type.IsPostgreSQL(): _, err := x.Exec("ALTER TABLE `user` ALTER COLUMN passwd_hash_algo SET DEFAULT 'argon2';") return err + case setting.Database.Type.IsMSSQL(): + // need to find the constraint and drop it, then recreate it. + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + res, err := sess.QueryString("SELECT [name] FROM sys.default_constraints WHERE parent_object_id=OBJECT_ID(?) AND COL_NAME(parent_object_id, parent_column_id)=?;", "user", "passwd_hash_algo") + if err != nil { + return err + } + if len(res) > 0 { + constraintName := res[0]["name"] + log.Error("Results of select constraint: %s", constraintName) + _, err := sess.Exec("ALTER TABLE [user] DROP CONSTRAINT " + constraintName) + if err != nil { + return err + } + _, err = sess.Exec("ALTER TABLE [user] ADD CONSTRAINT " + constraintName + " DEFAULT 'argon2' FOR passwd_hash_algo") + if err != nil { + return err + } + } else { + _, err := sess.Exec("ALTER TABLE [user] ADD DEFAULT('argon2') FOR passwd_hash_algo") + if err != nil { + return err + } + } + return sess.Commit() + case setting.Database.Type.IsSQLite3(): // drop through default: diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 2d688b1706..1094d8abf7 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -62,6 +62,13 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error { return err } + if setting.Database.Type.IsMSSQL() { + if _, err := sess.Exec(sqlSelect + " INTO #temp_comments" + sqlTail); err != nil { + log.Error("unable to create temporary table") + return err + } + } + comments := make([]*Comment, 0, batchSize) switch { @@ -71,6 +78,9 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error { fallthrough case setting.Database.Type.IsSQLite3(): sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start) + case setting.Database.Type.IsMSSQL(): + sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " + + "(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id" default: return fmt.Errorf("Unsupported database type") } diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go index 5b1a779294..926350cdf7 100644 --- a/models/migrations/v1_14/v165.go +++ b/models/migrations/v1_14/v165.go @@ -32,7 +32,13 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error { return err } - if _, err := x.Exec("UPDATE hook_task SET typ = TRIM(typ)"); err != nil { + var hookTaskTrimSQL string + if dbType == schemas.MSSQL { + hookTaskTrimSQL = "UPDATE hook_task SET typ = RTRIM(LTRIM(typ))" + } else { + hookTaskTrimSQL = "UPDATE hook_task SET typ = TRIM(typ)" + } + if _, err := x.Exec(hookTaskTrimSQL); err != nil { return err } @@ -52,6 +58,12 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error { return err } - _, err := x.Exec("UPDATE webhook SET type = TRIM(type)") + var webhookTrimSQL string + if dbType == schemas.MSSQL { + webhookTrimSQL = "UPDATE webhook SET type = RTRIM(LTRIM(type))" + } else { + webhookTrimSQL = "UPDATE webhook SET type = TRIM(type)" + } + _, err := x.Exec(webhookTrimSQL) return err } diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index 871c9db18a..4b3dd1467a 100644 --- a/models/migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -53,11 +53,16 @@ func RenameTaskErrorsToMessage(x *xorm.Engine) error { } } - if setting.Database.Type.IsMySQL() { + switch { + case setting.Database.Type.IsMySQL(): if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil { return err } - } else { + case setting.Database.Type.IsMSSQL(): + if _, err := sess.Exec("sp_rename 'task.errors', 'message', 'COLUMN'"); err != nil { + return err + } + default: if _, err := sess.Exec("ALTER TABLE `task` RENAME COLUMN errors TO message"); err != nil { return err } diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index db45b11aed..533bb4bf80 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -4,43 +4,18 @@ package v1_16 //nolint import ( - "crypto/ecdh" "encoding/base32" - "errors" "fmt" "strings" + "code.gitea.io/gitea/models/migrations/base" "code.gitea.io/gitea/modules/timeutil" + "github.com/tstranex/u2f" "xorm.io/xorm" "xorm.io/xorm/schemas" ) -func parseU2FRegistration(raw []byte) (pubKey *ecdh.PublicKey, keyHandle []byte, err error) { - if len(raw) < 69 { - return nil, nil, errors.New("data is too short") - } - if raw[0] != 0x05 { - return nil, nil, errors.New("invalid reserved byte") - } - raw = raw[1:] - - pubKey, err = ecdh.P256().NewPublicKey(raw[:65]) - if err != nil { - return nil, nil, err - } - raw = raw[65:] - - khLen := int(raw[0]) - if len(raw) < khLen { - return nil, nil, errors.New("invalid key handle") - } - raw = raw[1:] - keyHandle = raw[:khLen] - - return pubKey, keyHandle, nil -} - // v208 migration was completely broken func RemigrateU2FCredentials(x *xorm.Engine) error { // Create webauthnCredential table @@ -68,6 +43,31 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { if err != nil { return err } + case schemas.ORACLE: + _, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)") + if err != nil { + return err + } + case schemas.MSSQL: + // This column has an index on it. I could write all of the code to attempt to change the index OR + // I could just use recreate table. + sess := x.NewSession() + if err := sess.Begin(); err != nil { + _ = sess.Close() + return err + } + + if err := base.RecreateTable(sess, new(webauthnCredential)); err != nil { + _ = sess.Close() + return err + } + if err := sess.Commit(); err != nil { + _ = sess.Close() + return err + } + if err := sess.Close(); err != nil { + return err + } case schemas.POSTGRES: _, err := x.Exec("ALTER TABLE webauthn_credential ALTER COLUMN credential_id TYPE VARCHAR(410)") if err != nil { @@ -111,8 +111,18 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { if err := sess.Begin(); err != nil { return fmt.Errorf("unable to allow start session. Error: %w", err) } + if x.Dialect().URI().DBType == schemas.MSSQL { + if _, err := sess.Exec("SET IDENTITY_INSERT `webauthn_credential` ON"); err != nil { + return fmt.Errorf("unable to allow identity insert on webauthn_credential. Error: %w", err) + } + } for _, reg := range regs { - pubKey, keyHandle, err := parseU2FRegistration(reg.Raw) + parsed := new(u2f.Registration) + err = parsed.UnmarshalBinary(reg.Raw) + if err != nil { + continue + } + pubKey, err := parsed.PubKey.ECDH() if err != nil { continue } @@ -121,7 +131,7 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { Name: reg.Name, LowerName: strings.ToLower(reg.Name), UserID: reg.UserID, - CredentialID: base32.HexEncoding.EncodeToString(keyHandle), + CredentialID: base32.HexEncoding.EncodeToString(parsed.KeyHandle), PublicKey: pubKey.Bytes(), AttestationType: "fido-u2f", AAGUID: []byte{}, diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index b39646545a..d43fb03106 100644 --- a/models/migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -4,7 +4,6 @@ package v1_16 //nolint import ( - "encoding/hex" "testing" "code.gitea.io/gitea/models/migrations/base" @@ -14,19 +13,6 @@ import ( "xorm.io/xorm/schemas" ) -func TestParseU2FRegistration(t *testing.T) { - // test vectors from https://github.com/tstranex/u2f/blob/d21a03e0b1d9fc1df59ff54e7a513655c1748b0c/register_test.go#L15 - - const testRegRespHex = "0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871" - - regResp, err := hex.DecodeString(testRegRespHex) - assert.NoError(t, err) - pubKey, keyHandle, err := parseU2FRegistration(regResp) - assert.NoError(t, err) - assert.Equal(t, "04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9", hex.EncodeToString(pubKey.Bytes())) - assert.Equal(t, "2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25", hex.EncodeToString(keyHandle)) -} - func Test_RemigrateU2FCredentials(t *testing.T) { // Create webauthnCredential table type WebauthnCredential struct { diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go index d4007163ab..904ddc5192 100644 --- a/models/migrations/v1_17/v220.go +++ b/models/migrations/v1_17/v220.go @@ -12,10 +12,14 @@ import ( ) func AddContainerRepositoryProperty(x *xorm.Engine) (err error) { - if x.Dialect().URI().DBType == schemas.SQLITE { + switch x.Dialect().URI().DBType { + case schemas.SQLITE: _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer) - } else { + case schemas.MSSQL: + _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name + '/' + p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", + packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer) + default: _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer) } diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index 3592eb1be6..018451ee4c 100644 --- a/models/migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -64,11 +64,16 @@ func RenameCredentialIDBytes(x *xorm.Engine) error { } } - if setting.Database.Type.IsMySQL() { + switch { + case setting.Database.Type.IsMySQL(): if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil { return err } - } else { + case setting.Database.Type.IsMSSQL(): + if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil { + return err + } + default: if _, err := sess.Exec("ALTER TABLE `webauthn_credential` RENAME COLUMN credential_id_bytes TO credential_id"); err != nil { return err } diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go index b0d4c21502..ab58d12880 100644 --- a/models/migrations/v1_20/v245.go +++ b/models/migrations/v1_20/v245.go @@ -50,7 +50,8 @@ func RenameWebhookOrgToOwner(x *xorm.Engine) error { } } - if setting.Database.Type.IsMySQL() { + switch { + case setting.Database.Type.IsMySQL(): inferredTable, err := x.TableInfo(new(Webhook)) if err != nil { return err @@ -59,7 +60,11 @@ func RenameWebhookOrgToOwner(x *xorm.Engine) error { if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", sqlType)); err != nil { return err } - } else { + case setting.Database.Type.IsMSSQL(): + if _, err := sess.Exec("sp_rename 'webhook.org_id', 'owner_id', 'COLUMN'"); err != nil { + return err + } + default: if _, err := sess.Exec("ALTER TABLE `webhook` RENAME COLUMN org_id TO owner_id"); err != nil { return err } diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index 86388ef0b8..a09957b291 100644 --- a/models/migrations/v1_20/v250.go +++ b/models/migrations/v1_20/v250.go @@ -104,7 +104,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error { // Convert to new metadata format - newMetadata := &MetadataNew{ + new := &MetadataNew{ Type: old.Type, IsTagged: old.IsTagged, Platform: old.Platform, @@ -119,7 +119,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error { Manifests: manifests, } - metadataJSON, err := json.Marshal(newMetadata) + metadataJSON, err := json.Marshal(new) if err != nil { return err } diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 86946d1c39..0a45c51245 100644 --- a/models/migrations/v1_22/v283.go +++ b/models/migrations/v1_22/v283.go @@ -4,7 +4,10 @@ package v1_22 //nolint import ( + "fmt" + "xorm.io/xorm" + "xorm.io/xorm/schemas" ) func AddCombinedIndexToIssueUser(x *xorm.Engine) error { @@ -20,12 +23,18 @@ func AddCombinedIndexToIssueUser(x *xorm.Engine) error { return err } for _, issueUser := range duplicatedIssueUsers { - var ids []int64 - if err := x.SQL("SELECT id FROM issue_user WHERE issue_id = ? and uid = ? limit ?", issueUser.IssueID, issueUser.UID, issueUser.Cnt-1).Find(&ids); err != nil { - return err - } - if _, err := x.Table("issue_user").In("id", ids).Delete(); err != nil { - return err + if x.Dialect().URI().DBType == schemas.MSSQL { + if _, err := x.Exec(fmt.Sprintf("delete from issue_user where id in (SELECT top %d id FROM issue_user WHERE issue_id = ? and uid = ?)", issueUser.Cnt-1), issueUser.IssueID, issueUser.UID); err != nil { + return err + } + } else { + var ids []int64 + if err := x.SQL("SELECT id FROM issue_user WHERE issue_id = ? and uid = ? limit ?", issueUser.IssueID, issueUser.UID, issueUser.Cnt-1).Find(&ids); err != nil { + return err + } + if _, err := x.Table("issue_user").In("id", ids).Delete(); err != nil { + return err + } } } diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index 2d62d7788c..fbbd87344f 100644 --- a/models/migrations/v1_22/v286.go +++ b/models/migrations/v1_22/v286.go @@ -3,6 +3,7 @@ package v1_22 //nolint import ( + "errors" "fmt" "code.gitea.io/gitea/modules/log" @@ -32,10 +33,27 @@ func expandHashReferencesToSha256(x *xorm.Engine) error { } if !setting.Database.Type.IsSQLite3() { + if setting.Database.Type.IsMSSQL() { + // drop indexes that need to be re-created afterwards + droppedIndexes := []string{ + "DROP INDEX IF EXISTS [IDX_commit_status_context_hash] ON [commit_status]", + "DROP INDEX IF EXISTS [UQE_review_state_pull_commit_user] ON [review_state]", + "DROP INDEX IF EXISTS [UQE_repo_archiver_s] ON [repo_archiver]", + } + for _, s := range droppedIndexes { + _, err := db.Exec(s) + if err != nil { + return errors.New(s + " " + err.Error()) + } + } + } + for _, alts := range alteredTables { var err error if setting.Database.Type.IsMySQL() { _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) + } else if setting.Database.Type.IsMSSQL() { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1])) } else { _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1])) } @@ -43,6 +61,20 @@ func expandHashReferencesToSha256(x *xorm.Engine) error { return fmt.Errorf("alter column '%s' of table '%s' failed: %w", alts[1], alts[0], err) } } + + if setting.Database.Type.IsMSSQL() { + recreateIndexes := []string{ + "CREATE INDEX IDX_commit_status_context_hash ON commit_status(context_hash)", + "CREATE UNIQUE INDEX UQE_review_state_pull_commit_user ON review_state(user_id, pull_id, commit_sha)", + "CREATE UNIQUE INDEX UQE_repo_archiver_s ON repo_archiver(repo_id, type, commit_id)", + } + for _, s := range recreateIndexes { + _, err := db.Exec(s) + if err != nil { + return errors.New(s + " " + err.Error()) + } + } + } } log.Debug("Updated database tables to hold SHA256 git hash references") diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go deleted file mode 100644 index 314b4519f1..0000000000 --- a/models/migrations/v1_22/v294.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import ( - "xorm.io/xorm" -) - -// AddUniqueIndexForProjectIssue adds unique indexes for project issue table -func AddUniqueIndexForProjectIssue(x *xorm.Engine) error { - // remove possible duplicated records in table project_issue - type result struct { - IssueID int64 - ProjectID int64 - Cnt int - } - var results []result - if err := x.Select("issue_id, project_id, count(*) as cnt"). - Table("project_issue"). - GroupBy("issue_id, project_id"). - Having("count(*) > 1"). - Find(&results); err != nil { - return err - } - for _, r := range results { - var ids []int64 - if err := x.SQL("SELECT id FROM project_issue WHERE issue_id = ? and project_id = ? limit ?", r.IssueID, r.ProjectID, r.Cnt-1).Find(&ids); err != nil { - return err - } - if _, err := x.Table("project_issue").In("id", ids).Delete(); err != nil { - return err - } - } - - // add unique index for project_issue table - type ProjectIssue struct { //revive:disable-line:exported - ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX unique(s)"` - ProjectID int64 `xorm:"INDEX unique(s)"` - } - - return x.Sync(new(ProjectIssue)) -} diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go deleted file mode 100644 index 82a3bcd602..0000000000 --- a/models/migrations/v1_22/v294_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import ( - "slices" - "testing" - - "code.gitea.io/gitea/models/migrations/base" - - "github.com/stretchr/testify/assert" - "xorm.io/xorm/schemas" -) - -func Test_AddUniqueIndexForProjectIssue(t *testing.T) { - type ProjectIssue struct { //revive:disable-line:exported - ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX"` - ProjectID int64 `xorm:"INDEX"` - } - - // Prepare and load the testing database - x, deferable := base.PrepareTestEnv(t, 0, new(ProjectIssue)) - defer deferable() - if x == nil || t.Failed() { - return - } - - cnt, err := x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count() - assert.NoError(t, err) - assert.EqualValues(t, 2, cnt) - - assert.NoError(t, AddUniqueIndexForProjectIssue(x)) - - cnt, err = x.Table("project_issue").Where("project_id=1 AND issue_id=1").Count() - assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - tables, err := x.DBMetas() - assert.NoError(t, err) - assert.EqualValues(t, 1, len(tables)) - found := false - for _, index := range tables[0].Indexes { - if index.Type == schemas.UniqueType { - found = true - slices.Equal(index.Cols, []string{"project_id", "issue_id"}) - break - } - } - assert.True(t, found) -} diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go deleted file mode 100644 index 17bdadb4ad..0000000000 --- a/models/migrations/v1_22/v295.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import "xorm.io/xorm" - -func AddCommitStatusSummary(x *xorm.Engine) error { - 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 string `xorm:"VARCHAR(7) NOT NULL"` - } - // there is no migrations because if there is no data on this table, it will fall back to get data - // from commit status - return x.Sync2(new(CommitStatusSummary)) -} diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go deleted file mode 100644 index 1ecacab95f..0000000000 --- a/models/migrations/v1_22/v296.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import "xorm.io/xorm" - -func AddCommitStatusSummary2(x *xorm.Engine) error { - type CommitStatusSummary struct { - ID int64 `xorm:"pk autoincr"` - TargetURL string `xorm:"TEXT"` - } - // there is no migrations because if there is no data on this table, it will fall back to get data - // from commit status - return x.Sync(new(CommitStatusSummary)) -} diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go deleted file mode 100644 index b9f3b95ade..0000000000 --- a/models/migrations/v1_22/v298.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_22 //nolint - -import "xorm.io/xorm" - -func DropWronglyCreatedTable(x *xorm.Engine) error { - return x.DropTables("o_auth2_application") -} diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go deleted file mode 100644 index b7948bd4dd..0000000000 --- a/models/migrations/v1_23/main_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_23 //nolint - -import ( - "testing" - - "code.gitea.io/gitea/models/migrations/base" -) - -func TestMain(m *testing.M) { - base.MainTest(m) -} diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 586187228b..4e50ca9219 100644 --- a/models/migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -61,6 +61,7 @@ func AddScratchHash(x *xorm.Engine) error { if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil { return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err) } + } } diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go index 734fc24641..a100dc1ef7 100644 --- a/models/migrations/v1_8/v81.go +++ b/models/migrations/v1_8/v81.go @@ -18,6 +18,8 @@ func ChangeU2FCounterType(x *xorm.Engine) error { _, err = x.Exec("ALTER TABLE `u2f_registration` MODIFY `counter` BIGINT") case schemas.POSTGRES: _, err = x.Exec("ALTER TABLE `u2f_registration` ALTER COLUMN `counter` SET DATA TYPE bigint") + case schemas.MSSQL: + _, err = x.Exec("ALTER TABLE `u2f_registration` ALTER COLUMN `counter` BIGINT") } if err != nil { diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index a23d7c5d6e..9419ee1aae 100644 --- a/models/migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -81,6 +81,7 @@ func HashAppToken(x *xorm.Engine) error { if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil { return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err) } + } } diff --git a/models/organization/team.go b/models/organization/team.go index 1b737c2d3d..17db11c42d 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -222,8 +222,9 @@ func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreN if err != nil { if ignoreNonExistent { continue + } else { + return nil, err } - return nil, err } ids = append(ids, u.ID) } diff --git a/models/project/board.go b/models/project/board.go index 7faabc52c5..5f142a356c 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -110,11 +110,13 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error { var items []string switch project.BoardType { + case BoardTypeBugTriage: items = setting.Project.ProjectBoardBugTriageType case BoardTypeBasicKanban: items = setting.Project.ProjectBoardBasicKanbanType + case BoardTypeNone: fallthrough default: diff --git a/models/repo/archive_download_count.go b/models/repo/archive_download_count.go deleted file mode 100644 index 9c1382e0ac..0000000000 --- a/models/repo/archive_download_count.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "context" - - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/git" - api "code.gitea.io/gitea/modules/structs" -) - -// RepoArchiveDownloadCount counts all archive downloads for a tag -type RepoArchiveDownloadCount struct { //nolint:revive - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"index unique(s)"` - ReleaseID int64 `xorm:"index unique(s)"` - Type git.ArchiveType `xorm:"unique(s)"` - Count int64 -} - -func init() { - db.RegisterModel(new(RepoArchiveDownloadCount)) -} - -// CountArchiveDownload adds one download the the given archive -func CountArchiveDownload(ctx context.Context, repoID, releaseID int64, tp git.ArchiveType) error { - updateCount, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("release_id = ?", releaseID).And("`type` = ?", tp).Incr("count").Update(new(RepoArchiveDownloadCount)) - if err != nil { - return err - } - - if updateCount != 0 { - // The count was updated, so we can exit - return nil - } - - // The archive does not esxists in the databse, so let's add it - newCounter := &RepoArchiveDownloadCount{ - RepoID: repoID, - ReleaseID: releaseID, - Type: tp, - Count: 1, - } - - _, err = db.GetEngine(ctx).Insert(newCounter) - return err -} - -// GetArchiveDownloadCount returns the download count of a tag -func GetArchiveDownloadCount(ctx context.Context, repoID, releaseID int64) (*api.TagArchiveDownloadCount, error) { - downloadCountList := make([]RepoArchiveDownloadCount, 0) - err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("release_id = ?", releaseID).Find(&downloadCountList) - if err != nil { - return nil, err - } - - tagCounter := new(api.TagArchiveDownloadCount) - - for _, singleCount := range downloadCountList { - switch singleCount.Type { - case git.ZIP: - tagCounter.Zip = singleCount.Count - case git.TARGZ: - tagCounter.TarGz = singleCount.Count - } - } - - return tagCounter, nil -} - -// GetDownloadCountForTagName returns the download count of a tag with the given name -func GetArchiveDownloadCountForTagName(ctx context.Context, repoID int64, tagName string) (*api.TagArchiveDownloadCount, error) { - release, err := GetRelease(ctx, repoID, tagName) - if err != nil { - if IsErrReleaseNotExist(err) { - return new(api.TagArchiveDownloadCount), nil - } - return nil, err - } - - return GetArchiveDownloadCount(ctx, repoID, release.ID) -} - -// DeleteArchiveDownloadCountForRelease deletes the release from the repo_archive_download_count table -func DeleteArchiveDownloadCountForRelease(ctx context.Context, releaseID int64) error { - _, err := db.GetEngine(ctx).Delete(&RepoArchiveDownloadCount{ReleaseID: releaseID}) - return err -} diff --git a/models/repo/archive_download_count_test.go b/models/repo/archive_download_count_test.go deleted file mode 100644 index 53bdf9a1e0..0000000000 --- a/models/repo/archive_download_count_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo_test - -import ( - "testing" - - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRepoArchiveDownloadCount(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - release, err := repo_model.GetReleaseByID(db.DefaultContext, 1) - require.NoError(t, err) - - // We have no count, so it should return 0 - downloadCount, err := repo_model.GetArchiveDownloadCount(db.DefaultContext, release.RepoID, release.ID) - require.NoError(t, err) - assert.Equal(t, int64(0), downloadCount.Zip) - assert.Equal(t, int64(0), downloadCount.TarGz) - - // Set the TarGz counter to 1 - err = repo_model.CountArchiveDownload(db.DefaultContext, release.RepoID, release.ID, git.TARGZ) - require.NoError(t, err) - - downloadCount, err = repo_model.GetArchiveDownloadCountForTagName(db.DefaultContext, release.RepoID, release.TagName) - require.NoError(t, err) - assert.Equal(t, int64(0), downloadCount.Zip) - assert.Equal(t, int64(1), downloadCount.TarGz) - - // Set the TarGz counter to 2 - err = repo_model.CountArchiveDownload(db.DefaultContext, release.RepoID, release.ID, git.TARGZ) - require.NoError(t, err) - - downloadCount, err = repo_model.GetArchiveDownloadCountForTagName(db.DefaultContext, release.RepoID, release.TagName) - require.NoError(t, err) - assert.Equal(t, int64(0), downloadCount.Zip) - assert.Equal(t, int64(2), downloadCount.TarGz) - - // Set the Zip counter to 1 - err = repo_model.CountArchiveDownload(db.DefaultContext, release.RepoID, release.ID, git.ZIP) - require.NoError(t, err) - - downloadCount, err = repo_model.GetArchiveDownloadCountForTagName(db.DefaultContext, release.RepoID, release.TagName) - require.NoError(t, err) - assert.Equal(t, int64(1), downloadCount.Zip) - assert.Equal(t, int64(2), downloadCount.TarGz) - - // Delete the count - err = repo_model.DeleteArchiveDownloadCountForRelease(db.DefaultContext, release.ID) - require.NoError(t, err) - - downloadCount, err = repo_model.GetArchiveDownloadCountForTagName(db.DefaultContext, release.RepoID, release.TagName) - require.NoError(t, err) - assert.Equal(t, int64(0), downloadCount.Zip) - assert.Equal(t, int64(0), downloadCount.TarGz) -} diff --git a/models/repo/archiver.go b/models/repo/archiver.go index 3f05fcf752..14ffa1d89b 100644 --- a/models/repo/archiver.go +++ b/models/repo/archiver.go @@ -35,7 +35,6 @@ type RepoArchiver struct { //revive:disable-line:exported Status ArchiverStatus CommitID string `xorm:"VARCHAR(64) unique(s)"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` - ReleaseID int64 `xorm:"-"` } func init() { diff --git a/models/repo/attachment.go b/models/repo/attachment.go index 546e409de7..64df6b166e 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -24,7 +24,7 @@ type Attachment struct { IssueID int64 `xorm:"INDEX"` // maybe zero when creating ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added - CommentID int64 `xorm:"INDEX"` + CommentID int64 Name string DownloadCount int64 `xorm:"DEFAULT 0"` Size int64 `xorm:"DEFAULT 0"` diff --git a/models/repo/release.go b/models/repo/release.go index 075e287174..a9f65f6c3e 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -65,30 +65,28 @@ func (err ErrReleaseNotExist) Unwrap() error { // Release represents a release of repository. type Release struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX UNIQUE(n)"` - Repo *Repository `xorm:"-"` - PublisherID int64 `xorm:"INDEX"` - Publisher *user_model.User `xorm:"-"` - TagName string `xorm:"INDEX UNIQUE(n)"` - OriginalAuthor string - OriginalAuthorID int64 `xorm:"index"` - LowerTagName string - Target string - TargetBehind string `xorm:"-"` // to handle non-existing or empty target - Title string - Sha1 string `xorm:"VARCHAR(64)"` - HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` - NumCommits int64 - NumCommitsBehind int64 `xorm:"-"` - Note string `xorm:"TEXT"` - RenderedNote template.HTML `xorm:"-"` - 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 - Attachments []*Attachment `xorm:"-"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX"` - ArchiveDownloadCount *structs.TagArchiveDownloadCount `xorm:"-"` + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(n)"` + Repo *Repository `xorm:"-"` + PublisherID int64 `xorm:"INDEX"` + Publisher *user_model.User `xorm:"-"` + TagName string `xorm:"INDEX UNIQUE(n)"` + OriginalAuthor string + OriginalAuthorID int64 `xorm:"index"` + LowerTagName string + Target string + TargetBehind string `xorm:"-"` // to handle non-existing or empty target + Title string + Sha1 string `xorm:"VARCHAR(64)"` + NumCommits int64 + NumCommitsBehind int64 `xorm:"-"` + Note string `xorm:"TEXT"` + RenderedNote template.HTML `xorm:"-"` + 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 + Attachments []*Attachment `xorm:"-"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX"` } func init() { @@ -114,22 +112,9 @@ func (r *Release) LoadAttributes(ctx context.Context) error { } } } - - err = r.LoadArchiveDownloadCount(ctx) - if err != nil { - return err - } - return GetReleaseAttachments(ctx, r) } -// LoadArchiveDownloadCount loads the download count for the source archives -func (r *Release) LoadArchiveDownloadCount(ctx context.Context) error { - var err error - r.ArchiveDownloadCount, err = GetArchiveDownloadCount(ctx, r.RepoID, r.ID) - return err -} - // APIURL the api url for a release. release must have attributes loaded func (r *Release) APIURL() string { return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10) @@ -462,18 +447,6 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []s lowerTags = append(lowerTags, strings.ToLower(tag)) } - for _, tag := range tags { - release, err := GetRelease(ctx, repo.ID, tag) - if err != nil { - return fmt.Errorf("GetRelease: %w", err) - } - - err = DeleteArchiveDownloadCountForRelease(ctx, release.ID) - if err != nil { - return fmt.Errorf("DeleteTagArchiveDownloadCount: %w", err) - } - } - if _, err := db.GetEngine(ctx). Where("repo_id = ? AND is_tag = ?", repo.ID, true). In("lower_tag_name", lowerTags). diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 987c7df9b0..cb7cd47a8d 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -104,19 +104,18 @@ func (repos RepositoryList) LoadAttributes(ctx context.Context) error { return nil } - userIDs := container.FilterSlice(repos, func(repo *Repository) (int64, bool) { - return repo.OwnerID, true - }) + set := make(container.Set[int64]) repoIDs := make([]int64, len(repos)) for i := range repos { + set.Add(repos[i].OwnerID) repoIDs[i] = repos[i].ID } // Load owners. - users := make(map[int64]*user_model.User, len(userIDs)) + users := make(map[int64]*user_model.User, len(set)) if err := db.GetEngine(ctx). Where("id > 0"). - In("id", userIDs). + In("id", set.Values()). Find(&users); err != nil { return fmt.Errorf("find users: %w", err) } diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index d3fbd961bd..330b1e5546 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -95,7 +95,10 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us // and just waste 1 unit is cheaper than re-allocate memory once. users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) if len(userIDs) > 0 { - if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil { + if err = e.In("id", uniqueUserIDs.Values()). + Where(builder.Eq{"`user`.is_active": true}). + OrderBy(user_model.GetOrderByName()). + Find(&users); err != nil { return nil, err } } @@ -117,7 +120,8 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) return nil, err } - cond := builder.And(builder.Neq{"`user`.id": posterID}) + cond := builder.And(builder.Neq{"`user`.id": posterID}). + And(builder.Eq{"`user`.is_active": true}) if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate { // This a private repository: @@ -135,6 +139,7 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) // the owner of a private repo needs to be explicitly added. cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID}) } + } else { // This is a "public" repository: // Any user that has read access, is a watcher or organization member can be requested to review diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go index ad794beb9b..0433ff83d8 100644 --- a/models/repo/user_repo_test.go +++ b/models/repo/user_repo_test.go @@ -26,10 +26,17 @@ func TestRepoAssignees(t *testing.T) { repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}) users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21) assert.NoError(t, err) - assert.Len(t, users, 3) - assert.Equal(t, users[0].ID, int64(15)) - assert.Equal(t, users[1].ID, int64(18)) - assert.Equal(t, users[2].ID, int64(16)) + if assert.Len(t, users, 3) { + assert.ElementsMatch(t, []int64{15, 16, 18}, []int64{users[0].ID, users[1].ID, users[2].ID}) + } + + // do not return deactivated users + assert.NoError(t, user_model.UpdateUserCols(db.DefaultContext, &user_model.User{ID: 15, IsActive: false}, "is_active")) + users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21) + assert.NoError(t, err) + if assert.Len(t, users, 2) { + assert.NotContains(t, []int64{users[0].ID, users[1].ID}, 15) + } } func TestRepoGetReviewers(t *testing.T) { @@ -41,17 +48,19 @@ func TestRepoGetReviewers(t *testing.T) { ctx := db.DefaultContext reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2) assert.NoError(t, err) - assert.Len(t, reviewers, 4) + if assert.Len(t, reviewers, 3) { + assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID}) + } // should include doer if doer is not PR poster. reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 2) assert.NoError(t, err) - assert.Len(t, reviewers, 4) + assert.Len(t, reviewers, 3) // should not include PR poster, if PR poster would be otherwise eligible reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 4) assert.NoError(t, err) - assert.Len(t, reviewers, 3) + assert.Len(t, reviewers, 2) // test private user repo repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 0c23d759f9..f20c5bcdc0 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -5,7 +5,6 @@ package models import ( "context" - "errors" "fmt" "code.gitea.io/gitea/models/db" @@ -121,7 +120,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error { func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error { switch status { case repo_model.RepositoryBeingMigrated: - return errors.New("repo is not ready, currently migrating") + return fmt.Errorf("repo is not ready, currently migrating") case repo_model.RepositoryPendingTransfer: return ErrRepoTransferInProgress{} } diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 63b26a0af7..9ce0909589 100644 --- a/models/unittest/fixtures.go +++ b/models/unittest/fixtures.go @@ -60,6 +60,8 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { dialect = "postgres" case schemas.MYSQL: dialect = "mysql" + case schemas.MSSQL: + dialect = "mssql" case schemas.SQLITE: dialect = "sqlite3" default: diff --git a/models/user/fixtures/user.yml b/models/user/fixtures/user.yml deleted file mode 100644 index b1892f331b..0000000000 --- a/models/user/fixtures/user.yml +++ /dev/null @@ -1,36 +0,0 @@ -- - id: 1041 - lower_name: remote01 - name: remote01 - full_name: Remote01 - email: remote01@example.com - keep_email_private: false - email_notifications_preference: onmention - passwd: ZogKvWdyEx:password - passwd_hash_algo: dummy - must_change_password: false - login_source: 1001 - login_name: 123 - type: 5 - salt: ZogKvWdyEx - max_repo_creation: -1 - is_active: true - is_admin: false - is_restricted: false - allow_git_hook: false - allow_import_local: false - allow_create_organization: true - prohibit_login: true - avatar: avatarremote01 - avatar_email: avatarremote01@example.com - use_custom_avatar: false - num_followers: 0 - num_following: 0 - num_stars: 0 - num_repos: 0 - num_teams: 0 - num_members: 0 - visibility: 0 - repo_admin_change_team_access: false - theme: "" - keep_activity_private: false diff --git a/models/user/search.go b/models/user/search.go index 04c434e4fa..45b051187e 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -45,11 +45,7 @@ type SearchUserOptions struct { func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session { var cond builder.Cond - if opts.Type == UserTypeIndividual { - cond = builder.In("type", UserTypeIndividual, UserTypeRemoteUser) - } else { - cond = builder.Eq{"type": opts.Type} - } + cond = builder.Eq{"type": opts.Type} if opts.IncludeReserved { if opts.Type == UserTypeIndividual { cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or( @@ -144,7 +140,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String()) defer sessQuery.Close() - if opts.PageSize > 0 { + if opts.Page != 0 { sessQuery = db.SetSessionPagination(sessQuery, opts) } diff --git a/models/user/user.go b/models/user/user.go index 10c4915f5e..ff85c2cfa0 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -216,7 +216,7 @@ func (u *User) GetEmail() string { // GetAllUsers returns a slice of all individual users found in DB. func GetAllUsers(ctx context.Context) ([]*User, error) { users := make([]*User, 0) - return users, db.GetEngine(ctx).OrderBy("id").In("type", UserTypeIndividual, UserTypeRemoteUser).Find(&users) + return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users) } // GetAllAdmins returns a slice of all adminusers found in DB. @@ -416,10 +416,6 @@ func (u *User) IsBot() bool { return u.Type == UserTypeBot } -func (u *User) IsRemote() bool { - return u.Type == UserTypeRemoteUser -} - // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { @@ -922,8 +918,7 @@ func GetUserByName(ctx context.Context, name string) (*User, error) { if len(name) == 0 { return nil, ErrUserNotExist{Name: name} } - // adding Type: UserTypeIndividual is a noop because it is zero and discarded - u := &User{LowerName: strings.ToLower(name)} + u := &User{LowerName: strings.ToLower(name), Type: UserTypeIndividual} has, err := db.GetEngine(ctx).Get(u) if err != nil { return nil, err @@ -1007,8 +1002,9 @@ func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bo if err != nil { if ignoreNonExistent { continue + } else { + return nil, err } - return nil, err } ids = append(ids, u.ID) } @@ -1250,21 +1246,3 @@ func GetOrderByName() string { } return "name" } - -// IsFeatureDisabledWithLoginType checks if a user feature is disabled, taking into account the login type of the -// user if applicable -func IsFeatureDisabledWithLoginType(user *User, feature string) bool { - // NOTE: in the long run it may be better to check the ExternalLoginUser table rather than user.LoginType - return (user != nil && user.LoginType > auth.Plain && setting.Admin.ExternalUserDisableFeatures.Contains(feature)) || - setting.Admin.UserDisabledFeatures.Contains(feature) -} - -// DisabledFeaturesWithLoginType returns the set of user features disabled, taking into account the login type -// of the user if applicable -func DisabledFeaturesWithLoginType(user *User) *container.Set[string] { - // NOTE: in the long run it may be better to check the ExternalLoginUser table rather than user.LoginType - if user != nil && user.LoginType > auth.Plain { - return &setting.Admin.ExternalUserDisableFeatures - } - return &setting.Admin.UserDisabledFeatures -} diff --git a/models/user/user_test.go b/models/user/user_test.go index 4bf8c71369..bb72743301 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -5,8 +5,8 @@ package user_test import ( "context" - "crypto/rand" "fmt" + "math/rand" "strings" "testing" "time" @@ -16,12 +16,10 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password/hash" - "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) @@ -34,35 +32,6 @@ func TestOAuth2Application_LoadUser(t *testing.T) { assert.NotNil(t, user) } -func TestGetUserByName(t *testing.T) { - defer tests.AddFixtures("models/user/fixtures/")() - assert.NoError(t, unittest.PrepareTestDatabase()) - - { - _, err := user_model.GetUserByName(db.DefaultContext, "") - assert.True(t, user_model.IsErrUserNotExist(err), err) - } - { - _, err := user_model.GetUserByName(db.DefaultContext, "UNKNOWN") - assert.True(t, user_model.IsErrUserNotExist(err), err) - } - { - user, err := user_model.GetUserByName(db.DefaultContext, "USER2") - assert.NoError(t, err) - assert.Equal(t, user.Name, "user2") - } - { - user, err := user_model.GetUserByName(db.DefaultContext, "org3") - assert.NoError(t, err) - assert.Equal(t, user.Name, "org3") - } - { - user, err := user_model.GetUserByName(db.DefaultContext, "remote01") - assert.NoError(t, err) - assert.Equal(t, user.Name, "remote01") - } -} - func TestGetUserEmailsByNames(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) @@ -91,24 +60,7 @@ func TestCanCreateOrganization(t *testing.T) { assert.False(t, user.CanCreateOrganization()) } -func TestGetAllUsers(t *testing.T) { - defer tests.AddFixtures("models/user/fixtures/")() - assert.NoError(t, unittest.PrepareTestDatabase()) - - users, err := user_model.GetAllUsers(db.DefaultContext) - assert.NoError(t, err) - - found := make(map[user_model.UserType]bool, 0) - for _, user := range users { - found[user.Type] = true - } - assert.True(t, found[user_model.UserTypeIndividual], users) - assert.True(t, found[user_model.UserTypeRemoteUser], users) - assert.False(t, found[user_model.UserTypeOrganization], users) -} - func TestSearchUsers(t *testing.T) { - defer tests.AddFixtures("models/user/fixtures/")() assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) { users, _, err := user_model.SearchUsers(db.DefaultContext, opts) @@ -149,13 +101,13 @@ func TestSearchUsers(t *testing.T) { } testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)}, []int64{9}) testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, - []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) + []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) @@ -171,7 +123,7 @@ func TestSearchUsers(t *testing.T) { []int64{29}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)}, - []int64{1041, 37}) + []int64{37}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)}, []int64{24}) @@ -590,37 +542,3 @@ func Test_NormalizeUserFromEmail(t *testing.T) { } } } - -func TestDisabledUserFeatures(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - testValues := container.SetOf(setting.UserFeatureDeletion, - setting.UserFeatureManageSSHKeys, - setting.UserFeatureManageGPGKeys) - - oldSetting := setting.Admin.ExternalUserDisableFeatures - defer func() { - setting.Admin.ExternalUserDisableFeatures = oldSetting - }() - setting.Admin.ExternalUserDisableFeatures = testValues - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - - assert.Len(t, setting.Admin.UserDisabledFeatures.Values(), 0) - - // no features should be disabled with a plain login type - assert.LessOrEqual(t, user.LoginType, auth.Plain) - assert.Len(t, user_model.DisabledFeaturesWithLoginType(user).Values(), 0) - for _, f := range testValues.Values() { - assert.False(t, user_model.IsFeatureDisabledWithLoginType(user, f)) - } - - // check disabled features with external login type - user.LoginType = auth.OAuth2 - - // all features should be disabled - assert.NotEmpty(t, user_model.DisabledFeaturesWithLoginType(user).Values()) - for _, f := range testValues.Values() { - assert.True(t, user_model.IsFeatureDisabledWithLoginType(user, f)) - } -} diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index f3370f3db5..4ab806573b 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -361,15 +361,6 @@ func (w Webhook) HeaderAuthorization() (string, error) { return secret.DecryptSecret(setting.SecretKey, w.HeaderAuthorizationEncrypted) } -// HeaderAuthorizationTrimPrefix returns the decrypted Authorization with a specified prefix trimmed. -func (w Webhook) HeaderAuthorizationTrimPrefix(prefix string) (string, error) { - s, err := w.HeaderAuthorization() - if err != nil { - return "", err - } - return strings.TrimPrefix(s, prefix), nil -} - // SetHeaderAuthorization encrypts and sets the Authorization header. func (w *Webhook) SetHeaderAuthorization(cleartext string) error { if cleartext == "" { diff --git a/modules/auth/password/password.go b/modules/auth/password/password.go index 85f9780709..27074358a9 100644 --- a/modules/auth/password/password.go +++ b/modules/auth/password/password.go @@ -63,16 +63,16 @@ func NewComplexity() { func setupComplexity(values []string) { if len(values) != 1 || values[0] != "off" { for _, val := range values { - if complexity, ok := charComplexities[val]; ok { - validChars += complexity.ValidChars - requiredList = append(requiredList, complexity) + if complex, ok := charComplexities[val]; ok { + validChars += complex.ValidChars + requiredList = append(requiredList, complex) } } if len(requiredList) == 0 { // No valid character classes found; use all classes as default - for _, complexity := range charComplexities { - validChars += complexity.ValidChars - requiredList = append(requiredList, complexity) + for _, complex := range charComplexities { + validChars += complex.ValidChars + requiredList = append(requiredList, complex) } } } diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go index a2a6b3a174..f9deadc8d7 100644 --- a/modules/auth/password/pwn/pwn_test.go +++ b/modules/auth/password/pwn/pwn_test.go @@ -4,8 +4,9 @@ package pwn import ( - "math/rand/v2" + "math/rand" "net/http" + "os" "strings" "testing" "time" @@ -17,6 +18,11 @@ var client = New(WithHTTP(&http.Client{ Timeout: time.Second * 2, })) +func TestMain(m *testing.M) { + rand.Seed(time.Now().Unix()) + os.Exit(m.Run()) +} + func TestPassword(t *testing.T) { // Check input error _, err := client.CheckPassword("", false) @@ -75,24 +81,24 @@ func testPassword() string { // Set special character for i := 0; i < 5; i++ { - random := rand.IntN(len(specialCharSet)) + random := rand.Intn(len(specialCharSet)) password.WriteString(string(specialCharSet[random])) } // Set numeric for i := 0; i < 5; i++ { - random := rand.IntN(len(numberSet)) + random := rand.Intn(len(numberSet)) password.WriteString(string(numberSet[random])) } // Set uppercase for i := 0; i < 5; i++ { - random := rand.IntN(len(upperCharSet)) + random := rand.Intn(len(upperCharSet)) password.WriteString(string(upperCharSet[random])) } for i := 0; i < 5; i++ { - random := rand.IntN(len(allCharSet)) + random := rand.Intn(len(allCharSet)) password.WriteString(string(allCharSet[random])) } inRune := []rune(password.String()) diff --git a/modules/container/filter.go b/modules/container/filter.go deleted file mode 100644 index 37ec7c3d56..0000000000 --- a/modules/container/filter.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package container - -import "slices" - -// FilterSlice ranges over the slice and calls include() for each element. -// If the second returned value is true, the first returned value will be included in the resulting -// slice (after deduplication). -func FilterSlice[E any, T comparable](s []E, include func(E) (T, bool)) []T { - filtered := make([]T, 0, len(s)) // slice will be clipped before returning - seen := make(map[T]bool, len(s)) - for i := range s { - if v, ok := include(s[i]); ok && !seen[v] { - filtered = append(filtered, v) - seen[v] = true - } - } - return slices.Clip(filtered) -} diff --git a/modules/container/filter_test.go b/modules/container/filter_test.go deleted file mode 100644 index ad304e5abb..0000000000 --- a/modules/container/filter_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package container - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFilterMapUnique(t *testing.T) { - result := FilterSlice([]int{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - }, func(i int) (int, bool) { - switch i { - case 0: - return 0, true // included later - case 1: - return 0, true // duplicate of previous (should be ignored) - case 2: - return 2, false // not included - default: - return i, true - } - }) - assert.Equal(t, []int{0, 3, 4, 5, 6, 7, 8, 9}, result) -} diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index c988d6ab86..043dbb44bd 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -307,10 +307,10 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu // Deal with the binary hash idx = 0 - length := objectFormat.FullLength() / 2 - for idx < length { + len := objectFormat.FullLength() / 2 + for idx < len { var read int - read, err = rd.Read(shaBuf[idx:length]) + read, err = rd.Read(shaBuf[idx:len]) n += read if err != nil { return mode, fname, sha, n, err diff --git a/modules/git/commit.go b/modules/git/commit.go index b5ae2e0e52..00681e3b2f 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -462,7 +462,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) { _, _ = rd.Discard(1) } for { - modifier, err := rd.ReadString('\x00') + modifier, err := rd.ReadSlice('\x00') if err != nil { if err != io.EOF { log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err) diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go index 8e2523d7fb..49159cc418 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -49,8 +49,9 @@ readLoop: if len(line) > 0 && line[0] == ' ' { _, _ = signatureSB.Write(line[1:]) continue + } else { + pgpsig = false } - pgpsig = false } if !message { diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go index 8fa47a943c..0f865c52a8 100644 --- a/modules/git/diff_test.go +++ b/modules/git/diff_test.go @@ -159,6 +159,22 @@ func BenchmarkCutDiffAroundLine(b *testing.B) { } } +func ExampleCutDiffAroundLine() { + const diff = `diff --git a/README.md b/README.md +--- a/README.md ++++ b/README.md +@@ -1,3 +1,6 @@ + # gitea-github-migrator ++ ++ Build Status +- Latest Release + Docker Pulls ++ cut off ++ cut off` + result, _ := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3) + println(result) +} + func TestParseDiffHunkString(t *testing.T) { leftLine, leftHunk, rightLine, rightHunk := ParseDiffHunkString("@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER") assert.EqualValues(t, 19, leftLine) diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go index 349cfbd9ce..a3ee883968 100644 --- a/modules/git/pipeline/lfs_nogogit.go +++ b/modules/git/pipeline/lfs_nogogit.go @@ -213,6 +213,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err errChan <- err break } + } }() diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index e9f7454413..9b7b43d510 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -225,13 +225,13 @@ func TestGitAttributeCheckerError(t *testing.T) { ac, err := gitRepo.GitAttributeChecker("", "linguist-language") require.NoError(t, err) - // calling CheckPath before would allow git to cache part of it and successfully return later + // calling CheckPath before would allow git to cache part of it and succesfully return later assert.NoError(t, os.RemoveAll(gitRepo.Path)) _, err = ac.CheckPath("i-am-a-python.p") if err == nil { t.Skip( - "git check-attr started too fast and CheckPath was successful (and likely cached)", + "git check-attr started too fast and CheckPath was succesful (and likely cached)", "https://codeberg.org/forgejo/forgejo/issues/2948", ) } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index f9168bef7e..44273d2253 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -251,18 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) return nil, err } - length := objectFormat.FullLength() + len := objectFormat.FullLength() commits := []*Commit{} - shaline := make([]byte, length+1) + shaline := make([]byte, len+1) for { n, err := io.ReadFull(stdoutReader, shaline) - if err != nil || n < length { + if err != nil || n < len { if err == io.EOF { err = nil } return commits, err } - objectID, err := NewIDFromString(string(shaline[0:length])) + objectID, err := NewIDFromString(string(shaline[0:len])) if err != nil { return nil, err } diff --git a/modules/git/submodule.go b/modules/git/submodule.go index b99c81582b..37813ea4c7 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -64,6 +64,7 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { // ex: git@try.gitea.io:go-gitea/gitea match := scpSyntax.FindAllStringSubmatch(refURI, -1) if len(match) > 0 { + m := match[0] refHostname := m[2] pth := m[3] diff --git a/modules/git/tag.go b/modules/git/tag.go index 04f50e8db8..1fe4c16b5d 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -8,7 +8,6 @@ import ( "sort" "strings" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" ) @@ -21,14 +20,13 @@ const ( // Tag represents a Git tag. type Tag struct { - Name string - ID ObjectID - Object ObjectID // The id of this commit object - Type string - Tagger *Signature - Message string - Signature *ObjectSignature - ArchiveDownloadCount *api.TagArchiveDownloadCount + Name string + ID ObjectID + Object ObjectID // The id of this commit object + Type string + Tagger *Signature + Message string + Signature *ObjectSignature } // Commit return the commit of the tag reference diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 4ee47b7a13..d7ab3f7afd 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -216,8 +216,8 @@ func PlainText(code []byte) []template.HTML { } func formatLexerName(name string) string { - if name == "fallback" || name == "plaintext" { - return "Text" + if name == "fallback" { + return "Plaintext" } return util.ToTitleCaseNoLower(name) diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index dd15b97847..659688bd0f 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -58,7 +58,7 @@ func TestFile(t *testing.T) { name: "tags.txt", code: "<>", want: lines("<>"), - lexerName: "Text", + lexerName: "Plaintext", }, { name: "tags.py", diff --git a/modules/httplib/url_test.go b/modules/httplib/url_test.go index 2842edd514..72033b1208 100644 --- a/modules/httplib/url_test.go +++ b/modules/httplib/url_test.go @@ -7,39 +7,20 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) func TestIsRiskyRedirectURL(t *testing.T) { - defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")() - defer test.MockVariableValue(&setting.AppSubURL, "/sub")() - + setting.AppURL = "http://localhost:3000/" tests := []struct { input string want bool }{ {"", false}, {"foo", false}, - {"./", false}, - {"?key=val", false}, - {"/sub/", false}, - {"http://localhost:3000/sub/", false}, - {"/sub/foo", false}, - {"http://localhost:3000/sub/foo", false}, - {"http://localhost:3000/sub/test?param=false", false}, - // FIXME: should probably be true (would requires resolving references using setting.appURL.ResolveReference(u)) - {"/sub/../", false}, - {"http://localhost:3000/sub/../", false}, - {"/sUb/", false}, - {"http://localhost:3000/sUb/foo", false}, - {"/sub", false}, - {"/foo?k=%20#abc", false}, {"/", false}, - {"a/", false}, - {"test?param=false", false}, - {"/hey/hey/hey#3244", false}, + {"/foo?k=%20#abc", false}, {"//", true}, {"\\\\", true}, @@ -47,73 +28,7 @@ func TestIsRiskyRedirectURL(t *testing.T) { {"\\/", true}, {"mail:a@b.com", true}, {"https://test.com", true}, - {"http://localhost:3000/foo", true}, - {"http://localhost:3000/sub", true}, - {"http://localhost:3000/sub?key=val", true}, - {"https://example.com/", true}, - {"//example.com", true}, - {"http://example.com", true}, - {"http://localhost:3000/test?param=false", true}, - {"//localhost:3000/test?param=false", true}, - {"://missing protocol scheme", true}, - // FIXME: should probably be false - {"//localhost:3000/sub/test?param=false", true}, - } - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - assert.Equal(t, tt.want, IsRiskyRedirectURL(tt.input)) - }) - } -} - -func TestIsRiskyRedirectURLWithoutSubURL(t *testing.T) { - defer test.MockVariableValue(&setting.AppURL, "https://next.forgejo.org/")() - defer test.MockVariableValue(&setting.AppSubURL, "")() - - tests := []struct { - input string - want bool - }{ - {"", false}, - {"foo", false}, - {"./", false}, - {"?key=val", false}, - {"/sub/", false}, - {"https://next.forgejo.org/sub/", false}, - {"/sub/foo", false}, - {"https://next.forgejo.org/sub/foo", false}, - {"https://next.forgejo.org/sub/test?param=false", false}, - {"https://next.forgejo.org/sub/../", false}, - {"/sub/../", false}, - {"/sUb/", false}, - {"https://next.forgejo.org/sUb/foo", false}, - {"/sub", false}, - {"/foo?k=%20#abc", false}, - {"/", false}, - {"a/", false}, - {"test?param=false", false}, - {"/hey/hey/hey#3244", false}, - {"https://next.forgejo.org/test?param=false", false}, - {"https://next.forgejo.org/foo", false}, - {"https://next.forgejo.org/sub", false}, - {"https://next.forgejo.org/sub?key=val", false}, - - {"//", true}, - {"\\\\", true}, - {"/\\", true}, - {"\\/", true}, - {"mail:a@b.com", true}, - {"https://test.com", true}, - {"https://example.com/", true}, - {"//example.com", true}, - {"http://example.com", true}, - {"://missing protocol scheme", true}, - {"https://forgejo.org", true}, - {"https://example.org?url=https://next.forgejo.org", true}, - // FIXME: should probably be false - {"https://next.forgejo.org", true}, - {"//next.forgejo.org/test?param=false", true}, - {"//next.forgejo.org/sub/test?param=false", true}, + {setting.AppURL + "/foo", false}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 66724a3445..ff0e37ca29 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -193,6 +193,7 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error { batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize) if len(changes.Updates) > 0 { + // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil { log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) @@ -336,6 +337,7 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int if result, err = b.inner.Indexer.Search(facetRequest); err != nil { return 0, nil, nil, err } + } languagesFacet := result.Facets["languages"] for _, term := range languagesFacet.Terms.Terms() { diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 8f94088742..4a98b4588a 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.Paginator = opts.Paginator switch opts.SortType { - case "", "latest": + case "": searchOpt.SortBy = SortByCreatedDesc case "oldest": searchOpt.SortBy = SortByCreatedAsc @@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.SortBy = SortByDeadlineDesc case "priority", "priorityrepo", "project-column-sorting": // Unsupported sort type for search - fallthrough + searchOpt.SortBy = SortByUpdatedDesc default: searchOpt.SortBy = SortByUpdatedDesc } diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index c7cb59f2cf..53b383c8d5 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -145,6 +145,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query := elastic.NewBoolQuery() if options.Keyword != "" { + searchType := esMultiMatchTypePhrasePrefix if options.IsFuzzyKeyword { searchType = esMultiMatchTypeBestFields diff --git a/modules/log/event_format.go b/modules/log/event_format.go index d9dbebf831..524ca3dd87 100644 --- a/modules/log/event_format.go +++ b/modules/log/event_format.go @@ -125,6 +125,7 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms if mode.Colorize { buf = append(buf, resetBytes...) } + } if flags&(Lshortfile|Llongfile) != 0 { if mode.Colorize { diff --git a/modules/markup/html.go b/modules/markup/html.go index f73221a37f..56f63b8a76 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -54,7 +54,7 @@ var ( shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) // anySHA1Pattern splits url containing SHA into parts - anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`) + anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) // comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash" comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`) @@ -969,10 +969,10 @@ func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { subpath = node.Data[m[4]:m[5]] } - // 5th capture group matches a optional url hash + // 4th capture group matches a optional url hash hash := "" - if m[9] > 0 { - hash = node.Data[m[8]:m[9]][1:] + if m[7] > 0 { + hash = node.Data[m[6]:m[7]][1:] } start := m[0] diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 917f280c73..e313be7040 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -403,39 +403,28 @@ func TestRegExp_anySHA1Pattern(t *testing.T) { "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", - "", "#L2703", }, "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", "", - "", }, "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { "0705be475092aede1eddae01319ec931fb9c65fc", "", "", - "", }, "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { "0705be475092aede1eddae01319ec931fb9c65fc", "/src", "", - "", }, "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { "d8a994ef243349f321568f9e36d5c3f444b99cae", "", - "", "#diff-2", }, - "https://codeberg.org/forgejo/forgejo/src/commit/949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0/RELEASE-NOTES.md?display=source&w=1#L7-L9": { - "949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0", - "/RELEASE-NOTES.md", - "?display=source&w=1", - "#L7-L9", - }, } for k, v := range testCases { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index cfd1a66a18..a1a99c1a7f 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -127,12 +127,6 @@ func TestRender_CrossReferences(t *testing.T) { test( util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), `

gogitea/some-repo-name#12345

`) - - sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" - urlWithQuery := util.URLJoin(markup.TestAppURL, "forgejo", "some-repo-name", "commit", sha, "README.md") + "?display=source#L1-L5" - test( - urlWithQuery, - `

`+sha[:10]+`/README.md (L1-L5)

`) } func TestMisc_IsSameDomain(t *testing.T) { diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 8289e28677..5a481a31fd 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -4,12 +4,17 @@ package markdown import ( + "bytes" "fmt" "regexp" + "slices" "strings" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/setting" + giteautil "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" @@ -25,12 +30,6 @@ var byteMailto = []byte("mailto:") // ASTTransformer is a default transformer of the goldmark tree. type ASTTransformer struct{} -func (g *ASTTransformer) applyElementDir(n ast.Node) { - if markup.DefaultProcessorHelper.ElementDir != "" { - n.SetAttributeString("dir", []byte(markup.DefaultProcessorHelper.ElementDir)) - } -} - // Transform transforms the given AST tree. func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { firstChild := node.FirstChild() @@ -47,6 +46,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa tocMode = rc.TOC } + applyElementDir := func(n ast.Node) { + if markup.DefaultProcessorHelper.ElementDir != "" { + n.SetAttributeString("dir", []byte(markup.DefaultProcessorHelper.ElementDir)) + } + } + _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkContinue, nil @@ -54,15 +59,135 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa switch v := n.(type) { case *ast.Heading: - g.transformHeading(ctx, v, reader, &tocList) + for _, attr := range v.Attributes() { + if _, ok := attr.Value.([]byte); !ok { + v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) + } + } + txt := n.Text(reader.Source()) + header := markup.Header{ + Text: util.BytesToReadOnlyString(txt), + Level: v.Level, + } + if id, found := v.AttributeString("id"); found { + header.ID = util.BytesToReadOnlyString(id.([]byte)) + } + tocList = append(tocList, header) + applyElementDir(v) case *ast.Paragraph: - g.applyElementDir(v) + applyElementDir(v) case *ast.Image: - g.transformImage(ctx, v, reader) + // Images need two things: + // + // 1. Their src needs to munged to be a real value + // 2. If they're not wrapped with a link they need a link wrapper + + // Check if the destination is a real link + if len(v.Destination) > 0 && !markup.IsLink(v.Destination) { + v.Destination = []byte(giteautil.URLJoin( + ctx.Links.ResolveMediaLink(ctx.IsWiki), + strings.TrimLeft(string(v.Destination), "/"), + )) + } + + parent := n.Parent() + // Create a link around image only if parent is not already a link + if _, ok := parent.(*ast.Link); !ok && parent != nil { + next := n.NextSibling() + + // Create a link wrapper + wrap := ast.NewLink() + wrap.Destination = v.Destination + wrap.Title = v.Title + wrap.SetAttributeString("target", []byte("_blank")) + + // Duplicate the current image node + image := ast.NewImage(ast.NewLink()) + image.Destination = v.Destination + image.Title = v.Title + for _, attr := range v.Attributes() { + image.SetAttribute(attr.Name, attr.Value) + } + for child := v.FirstChild(); child != nil; { + next := child.NextSibling() + image.AppendChild(image, child) + child = next + } + + // Append our duplicate image to the wrapper link + wrap.AppendChild(wrap, image) + + // Wire in the next sibling + wrap.SetNextSibling(next) + + // Replace the current node with the wrapper link + parent.ReplaceChild(parent, n, wrap) + + // But most importantly ensure the next sibling is still on the old image too + v.SetNextSibling(next) + } case *ast.Link: - g.transformLink(ctx, v, reader) + // Links need their href to munged to be a real value + link := v.Destination + + // Do not process the link if it's not a link, starts with an hashtag + // (indicating it's an anchor link), starts with `mailto:` or any of the + // custom markdown URLs. + processLink := len(link) > 0 && !markup.IsLink(link) && + link[0] != '#' && !bytes.HasPrefix(link, byteMailto) && + !slices.ContainsFunc(setting.Markdown.CustomURLSchemes, func(s string) bool { + return bytes.HasPrefix(link, []byte(s+":")) + }) + + if processLink { + var base string + if ctx.IsWiki { + base = ctx.Links.WikiLink() + } else if ctx.Links.HasBranchInfo() { + base = ctx.Links.SrcLink() + } else { + base = ctx.Links.Base + } + + link = []byte(giteautil.URLJoin(base, string(link))) + } + if len(link) > 0 && link[0] == '#' { + link = []byte("#user-content-" + string(link)[1:]) + } + v.Destination = link case *ast.List: - g.transformList(ctx, v, reader, rc) + if v.HasChildren() { + children := make([]ast.Node, 0, v.ChildCount()) + child := v.FirstChild() + for child != nil { + children = append(children, child) + child = child.NextSibling() + } + v.RemoveChildren(v) + + for _, child := range children { + listItem := child.(*ast.ListItem) + if !child.HasChildren() || !child.FirstChild().HasChildren() { + v.AppendChild(v, child) + continue + } + taskCheckBox, ok := child.FirstChild().FirstChild().(*east.TaskCheckBox) + if !ok { + v.AppendChild(v, child) + continue + } + newChild := NewTaskCheckBoxListItem(listItem) + newChild.IsChecked = taskCheckBox.IsChecked + newChild.SetAttributeString("class", []byte("task-list-item")) + segments := newChild.FirstChild().Lines() + if segments.Len() > 0 { + segment := segments.At(0) + newChild.SourcePosition = rc.metaLength + segment.Start + } + v.AppendChild(v, newChild) + } + } + applyElementDir(v) case *ast.Text: if v.SoftLineBreak() && !v.HardLineBreak() { if ctx.Metas["mode"] != "document" { @@ -72,7 +197,10 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } } case *ast.CodeSpan: - g.transformCodeSpan(ctx, v, reader) + colorContent := n.Text(reader.Source()) + if matchColor(strings.ToLower(string(colorContent))) { + v.AppendChild(v, NewColorPreview(colorContent)) + } } return ast.WalkContinue, nil }) @@ -94,6 +222,50 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } } +type prefixedIDs struct { + values container.Set[string] +} + +// Generate generates a new element id. +func (p *prefixedIDs) Generate(value []byte, kind ast.NodeKind) []byte { + dft := []byte("id") + if kind == ast.KindHeading { + dft = []byte("heading") + } + return p.GenerateWithDefault(value, dft) +} + +// Generate generates a new element id. +func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { + result := common.CleanValue(value) + if len(result) == 0 { + result = dft + } + if !bytes.HasPrefix(result, []byte("user-content-")) { + result = append([]byte("user-content-"), result...) + } + if p.values.Add(util.BytesToReadOnlyString(result)) { + return result + } + for i := 1; ; i++ { + newResult := fmt.Sprintf("%s-%d", result, i) + if p.values.Add(newResult) { + return []byte(newResult) + } + } +} + +// Put puts a given element id to the used ids table. +func (p *prefixedIDs) Put(value []byte) { + p.values.Add(util.BytesToReadOnlyString(value)) +} + +func newPrefixedIDs() *prefixedIDs { + return &prefixedIDs{ + values: make(container.Set[string]), + } +} + // NewHTMLRenderer creates a HTMLRenderer to render // in the gitea form. func NewHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { @@ -123,6 +295,38 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox) } +// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements. +// See #21474 for reference +func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { + if entering { + if n.Attributes() != nil { + _, _ = w.WriteString("') + } else { + _, _ = w.WriteString("") + } + for c := n.FirstChild(); c != nil; c = c.NextSibling() { + switch v := c.(type) { + case *ast.Text: + segment := v.Segment + value := segment.Value(source) + if bytes.HasSuffix(value, []byte("\n")) { + r.Writer.RawWrite(w, value[:len(value)-1]) + r.Writer.RawWrite(w, []byte(" ")) + } else { + r.Writer.RawWrite(w, value) + } + case *ColorPreview: + _, _ = w.WriteString(fmt.Sprintf(``, string(v.Color))) + } + } + return ast.WalkSkipChildren, nil + } + _, _ = w.WriteString("") + return ast.WalkContinue, nil +} + func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { n := node.(*ast.Document) @@ -211,3 +415,38 @@ func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node return ast.WalkContinue, nil } + +func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + n := node.(*TaskCheckBoxListItem) + if entering { + if n.Attributes() != nil { + _, _ = w.WriteString("') + } else { + _, _ = w.WriteString("
  • ") + } + fmt.Fprintf(w, ``) + } else { + _ = w.WriteByte('>') + } + fc := n.FirstChild() + if fc != nil { + if _, ok := fc.(*ast.TextBlock); !ok { + _ = w.WriteByte('\n') + } + } + } else { + _, _ = w.WriteString("
  • \n") + } + return ast.WalkContinue, nil +} + +func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + return ast.WalkContinue, nil +} diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index 278494d95c..3e1d75b291 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -460,6 +460,7 @@ func TestColorPreview(t *testing.T) { res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) + } negativeTests := []string{ @@ -554,6 +555,7 @@ func TestMathBlock(t *testing.T) { res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) + } } diff --git a/modules/markup/markdown/prefixed_id.go b/modules/markup/markdown/prefixed_id.go deleted file mode 100644 index 9c60949202..0000000000 --- a/modules/markup/markdown/prefixed_id.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "bytes" - "fmt" - - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/markup/common" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/util" -) - -type prefixedIDs struct { - values container.Set[string] -} - -// Generate generates a new element id. -func (p *prefixedIDs) Generate(value []byte, kind ast.NodeKind) []byte { - dft := []byte("id") - if kind == ast.KindHeading { - dft = []byte("heading") - } - return p.GenerateWithDefault(value, dft) -} - -// GenerateWithDefault generates a new element id. -func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { - result := common.CleanValue(value) - if len(result) == 0 { - result = dft - } - if !bytes.HasPrefix(result, []byte("user-content-")) { - result = append([]byte("user-content-"), result...) - } - if p.values.Add(util.BytesToReadOnlyString(result)) { - return result - } - for i := 1; ; i++ { - newResult := fmt.Sprintf("%s-%d", result, i) - if p.values.Add(newResult) { - return []byte(newResult) - } - } -} - -// Put puts a given element id to the used ids table. -func (p *prefixedIDs) Put(value []byte) { - p.values.Add(util.BytesToReadOnlyString(value)) -} - -func newPrefixedIDs() *prefixedIDs { - return &prefixedIDs{ - values: make(container.Set[string]), - } -} diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go deleted file mode 100644 index 0cf1169dee..0000000000 --- a/modules/markup/markdown/transform_codespan.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "bytes" - "fmt" - "strings" - - "code.gitea.io/gitea/modules/markup" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" -) - -// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements. -// See #21474 for reference -func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { - if entering { - if n.Attributes() != nil { - _, _ = w.WriteString("') - } else { - _, _ = w.WriteString("") - } - for c := n.FirstChild(); c != nil; c = c.NextSibling() { - switch v := c.(type) { - case *ast.Text: - segment := v.Segment - value := segment.Value(source) - if bytes.HasSuffix(value, []byte("\n")) { - r.Writer.RawWrite(w, value[:len(value)-1]) - r.Writer.RawWrite(w, []byte(" ")) - } else { - r.Writer.RawWrite(w, value) - } - case *ColorPreview: - _, _ = w.WriteString(fmt.Sprintf(``, string(v.Color))) - } - } - return ast.WalkSkipChildren, nil - } - _, _ = w.WriteString("") - return ast.WalkContinue, nil -} - -func (g *ASTTransformer) transformCodeSpan(ctx *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) { - colorContent := v.Text(reader.Source()) - if matchColor(strings.ToLower(string(colorContent))) { - v.AppendChild(v, NewColorPreview(colorContent)) - } -} diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go deleted file mode 100644 index ce585a37de..0000000000 --- a/modules/markup/markdown/transform_heading.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "fmt" - - "code.gitea.io/gitea/modules/markup" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" -) - -func (g *ASTTransformer) transformHeading(ctx *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) { - for _, attr := range v.Attributes() { - if _, ok := attr.Value.([]byte); !ok { - v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) - } - } - txt := v.Text(reader.Source()) - header := markup.Header{ - Text: util.BytesToReadOnlyString(txt), - Level: v.Level, - } - if id, found := v.AttributeString("id"); found { - header.ID = util.BytesToReadOnlyString(id.([]byte)) - } - *tocList = append(*tocList, header) - g.applyElementDir(v) -} diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go deleted file mode 100644 index db449e9dd9..0000000000 --- a/modules/markup/markdown/transform_image.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "strings" - - "code.gitea.io/gitea/modules/markup" - giteautil "code.gitea.io/gitea/modules/util" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" -) - -func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image, reader text.Reader) { - // Images need two things: - // - // 1. Their src needs to munged to be a real value - // 2. If they're not wrapped with a link they need a link wrapper - - // Check if the destination is a real link - if len(v.Destination) > 0 && !markup.IsLink(v.Destination) { - v.Destination = []byte(giteautil.URLJoin( - ctx.Links.ResolveMediaLink(ctx.IsWiki), - strings.TrimLeft(string(v.Destination), "/"), - )) - } - - parent := v.Parent() - // Create a link around image only if parent is not already a link - if _, ok := parent.(*ast.Link); !ok && parent != nil { - next := v.NextSibling() - - // Create a link wrapper - wrap := ast.NewLink() - wrap.Destination = v.Destination - wrap.Title = v.Title - wrap.SetAttributeString("target", []byte("_blank")) - - // Duplicate the current image node - image := ast.NewImage(ast.NewLink()) - image.Destination = v.Destination - image.Title = v.Title - for _, attr := range v.Attributes() { - image.SetAttribute(attr.Name, attr.Value) - } - for child := v.FirstChild(); child != nil; { - next := child.NextSibling() - image.AppendChild(image, child) - child = next - } - - // Append our duplicate image to the wrapper link - wrap.AppendChild(wrap, image) - - // Wire in the next sibling - wrap.SetNextSibling(next) - - // Replace the current node with the wrapper link - parent.ReplaceChild(parent, v, wrap) - - // But most importantly ensure the next sibling is still on the old image too - v.SetNextSibling(next) - } -} diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go deleted file mode 100644 index aceae5b74f..0000000000 --- a/modules/markup/markdown/transform_link.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "bytes" - "slices" - - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" - giteautil "code.gitea.io/gitea/modules/util" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" -) - -func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, reader text.Reader) { - // Links need their href to munged to be a real value - link := v.Destination - - // Do not process the link if it's not a link, starts with an hashtag - // (indicating it's an anchor link), starts with `mailto:` or any of the - // custom markdown URLs. - processLink := len(link) > 0 && !markup.IsLink(link) && - link[0] != '#' && !bytes.HasPrefix(link, byteMailto) && - !slices.ContainsFunc(setting.Markdown.CustomURLSchemes, func(s string) bool { - return bytes.HasPrefix(link, []byte(s+":")) - }) - - if processLink { - var base string - if ctx.IsWiki { - base = ctx.Links.WikiLink() - } else if ctx.Links.HasBranchInfo() { - base = ctx.Links.SrcLink() - } else { - base = ctx.Links.Base - } - - link = []byte(giteautil.URLJoin(base, string(link))) - } - if len(link) > 0 && link[0] == '#' { - link = []byte("#user-content-" + string(link)[1:]) - } - v.Destination = link -} diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go deleted file mode 100644 index 6563e2dd64..0000000000 --- a/modules/markup/markdown/transform_list.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "fmt" - - "code.gitea.io/gitea/modules/markup" - - "github.com/yuin/goldmark/ast" - east "github.com/yuin/goldmark/extension/ast" - "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" -) - -func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - n := node.(*TaskCheckBoxListItem) - if entering { - if n.Attributes() != nil { - _, _ = w.WriteString("') - } else { - _, _ = w.WriteString("
  • ") - } - fmt.Fprintf(w, ``) - } else { - _ = w.WriteByte('>') - } - fc := n.FirstChild() - if fc != nil { - if _, ok := fc.(*ast.TextBlock); !ok { - _ = w.WriteByte('\n') - } - } - } else { - _, _ = w.WriteString("
  • \n") - } - return ast.WalkContinue, nil -} - -func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - return ast.WalkContinue, nil -} - -func (g *ASTTransformer) transformList(ctx *markup.RenderContext, v *ast.List, reader text.Reader, rc *RenderConfig) { - if v.HasChildren() { - children := make([]ast.Node, 0, v.ChildCount()) - child := v.FirstChild() - for child != nil { - children = append(children, child) - child = child.NextSibling() - } - v.RemoveChildren(v) - - for _, child := range children { - listItem := child.(*ast.ListItem) - if !child.HasChildren() || !child.FirstChild().HasChildren() { - v.AppendChild(v, child) - continue - } - taskCheckBox, ok := child.FirstChild().FirstChild().(*east.TaskCheckBox) - if !ok { - v.AppendChild(v, child) - continue - } - newChild := NewTaskCheckBoxListItem(listItem) - newChild.IsChecked = taskCheckBox.IsChecked - newChild.SetAttributeString("class", []byte("task-list-item")) - segments := newChild.FirstChild().Lines() - if segments.Len() > 0 { - segment := segments.At(0) - newChild.SourcePosition = rc.metaLength + segment.Start - } - v.AppendChild(v, newChild) - } - } - g.applyElementDir(v) -} diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 391ee6c12b..8efe4e395d 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -147,6 +147,7 @@ func (r *Writer) resolveLink(node org.Node) string { } if len(link) > 0 && !markup.IsLinkStr(link) && link[0] != '#' && !strings.HasPrefix(link, mailto) { + var base string if r.Ctx.IsWiki { base = r.Ctx.Links.WikiLink() diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index b120a0edf6..6688e78cd1 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { return nil } -func (o Option[T]) MarshalYAML() (any, error) { +func (o Option[T]) MarshalYAML() (interface{}, error) { if !o.Has() { return nil, nil } diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go index 4e6a5fc5f8..8878dcf973 100644 --- a/modules/packages/rubygems/marshal.go +++ b/modules/packages/rubygems/marshal.go @@ -147,35 +147,35 @@ func (e *MarshalEncoder) marshalIntInternal(i int64) error { return e.w.WriteByte(byte(i - 5)) } - var length int + var len int if 122 < i && i <= 0xff { - length = 1 + len = 1 } else if 0xff < i && i <= 0xffff { - length = 2 + len = 2 } else if 0xffff < i && i <= 0xffffff { - length = 3 + len = 3 } else if 0xffffff < i && i <= 0x3fffffff { - length = 4 + len = 4 } else if -0x100 <= i && i < -123 { - length = -1 + len = -1 } else if -0x10000 <= i && i < -0x100 { - length = -2 + len = -2 } else if -0x1000000 <= i && i < -0x100000 { - length = -3 + len = -3 } else if -0x40000000 <= i && i < -0x1000000 { - length = -4 + len = -4 } else { return ErrInvalidIntRange } - if err := e.w.WriteByte(byte(length)); err != nil { + if err := e.w.WriteByte(byte(len)); err != nil { return err } - if length < 0 { - length = -length + if len < 0 { + len = -len } - for c := 0; c < length; c++ { + for c := 0; c < len; c++ { if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil { return err } @@ -244,13 +244,13 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error { return err } - length := arr.Len() + len := arr.Len() - if err := e.marshalIntInternal(int64(length)); err != nil { + if err := e.marshalIntInternal(int64(len)); err != nil { return err } - for i := 0; i < length; i++ { + for i := 0; i < len; i++ { if err := e.marshal(arr.Index(i).Interface()); err != nil { return err } diff --git a/modules/process/manager_stacktraces.go b/modules/process/manager_stacktraces.go index e260893113..49bd5071f6 100644 --- a/modules/process/manager_stacktraces.go +++ b/modules/process/manager_stacktraces.go @@ -339,6 +339,7 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int } sort.Slice(processes, after(processes)) if !flat { + var sortChildren func(process *Process) sortChildren = func(process *Process) { diff --git a/modules/repository/temp.go b/modules/repository/temp.go index 04faa9db3d..53646718e0 100644 --- a/modules/repository/temp.go +++ b/modules/repository/temp.go @@ -32,6 +32,7 @@ func CreateTemporaryPath(prefix string) (string, error) { if err != nil { log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err) return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err) + } return basePath, nil } diff --git a/modules/setting/admin.go b/modules/setting/admin.go index 07b8ef0899..35ffa9efbf 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -3,9 +3,7 @@ package setting -import ( - "code.gitea.io/gitea/modules/container" -) +import "code.gitea.io/gitea/modules/container" // Admin settings var Admin struct { @@ -13,7 +11,6 @@ var Admin struct { DefaultEmailNotification string SendNotificationEmailOnNewUser bool UserDisabledFeatures container.Set[string] - ExternalUserDisableFeatures container.Set[string] } func loadAdminFrom(rootCfg ConfigProvider) { @@ -21,7 +18,6 @@ func loadAdminFrom(rootCfg ConfigProvider) { Admin.DisableRegularOrgCreation = sec.Key("DISABLE_REGULAR_ORG_CREATION").MustBool(false) Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") Admin.UserDisabledFeatures = container.SetOf(sec.Key("USER_DISABLED_FEATURES").Strings(",")...) - Admin.ExternalUserDisableFeatures = container.SetOf(sec.Key("EXTERNAL_USER_DISABLE_FEATURES").Strings(",")...) } const ( diff --git a/modules/setting/database.go b/modules/setting/database.go index 76fae27164..7433896455 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -12,13 +12,15 @@ import ( "path/filepath" "strings" "time" + + "code.gitea.io/gitea/modules/log" ) var ( // SupportedDatabaseTypes includes all XORM supported databases type, sqlite3 maybe added by `database_sqlite3.go` - SupportedDatabaseTypes = []string{"mysql", "postgres"} + SupportedDatabaseTypes = []string{"mysql", "postgres", "mssql"} // DatabaseTypeNames contains the friendly names for all database types - DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "sqlite3": "SQLite3"} + DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "mssql": "MSSQL", "sqlite3": "SQLite3"} // EnableSQLite3 use SQLite3, set by build flag EnableSQLite3 bool @@ -62,6 +64,11 @@ func loadDBSetting(rootCfg ConfigProvider) { sec := rootCfg.Section("database") Database.Type = DatabaseType(sec.Key("DB_TYPE").String()) + if Database.Type.IsMSSQL() { + log.Error("Your Forgejo instance uses Microsoft SQL Server as its database which is scheduled for removal in v8.0. Please file an issue https://codeberg.org/forgejo/forgejo/issues/new to get help migrating to another database. Waiting 60 seconds before starting Forgejo") + time.Sleep(time.Second * 60) + } + Database.Host = sec.Key("HOST").String() Database.Name = sec.Key("NAME").String() Database.User = sec.Key("USER").String() @@ -83,7 +90,7 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0) } Database.ConnMaxIdleTime = sec.Key("CONN_MAX_IDLETIME").MustDuration(0) - Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(100) + Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) Database.LogSQL = sec.Key("LOG_SQL").MustBool(false) @@ -120,6 +127,9 @@ func DBConnStr() (string, error) { Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, tls) case "postgres": connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode) + case "mssql": + host, port := ParseMSSQLHostPort(Database.Host) + connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd) case "sqlite3": if !EnableSQLite3 { return "", errors.New("this Gitea binary was not built with SQLite3 support") @@ -185,6 +195,28 @@ func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode s return connURL.String() } +// ParseMSSQLHostPort splits the host into host and port +func ParseMSSQLHostPort(info string) (string, string) { + // the default port "0" might be related to MSSQL's dynamic port, maybe it should be double-confirmed in the future + host, port := "127.0.0.1", "0" + if strings.Contains(info, ":") { + host = strings.Split(info, ":")[0] + port = strings.Split(info, ":")[1] + } else if strings.Contains(info, ",") { + host = strings.Split(info, ",")[0] + port = strings.TrimSpace(strings.Split(info, ",")[1]) + } else if len(info) > 0 { + host = info + } + if host == "" { + host = "127.0.0.1" + } + if port == "" { + port = "0" + } + return host, port +} + type DatabaseType string func (t DatabaseType) String() string { @@ -199,6 +231,10 @@ func (t DatabaseType) IsMySQL() bool { return t == "mysql" } +func (t DatabaseType) IsMSSQL() bool { + return t == "mssql" +} + func (t DatabaseType) IsPostgreSQL() bool { return t == "postgres" } diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 314fdea137..75337a312f 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -38,24 +38,6 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { return } - // Handle aliases - sec := rootCfg.Section("email.incoming") - if sec.HasKey("USER") && !sec.HasKey("USERNAME") { - IncomingEmail.Username = sec.Key("USER").String() - } - if sec.HasKey("PASSWD") && !sec.HasKey("PASSWORD") { - IncomingEmail.Password = sec.Key("PASSWD").String() - } - - // Infer Port if not set - if IncomingEmail.Port == 0 { - if IncomingEmail.UseTLS { - IncomingEmail.Port = 993 - } else { - IncomingEmail.Port = 143 - } - } - if err := checkReplyToAddress(IncomingEmail.ReplyToAddress); err != nil { log.Fatal("Invalid incoming_mail.REPLY_TO_ADDRESS (%s): %v", IncomingEmail.ReplyToAddress, err) } diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go deleted file mode 100644 index 0fdd44d333..0000000000 --- a/modules/setting/incoming_email_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package setting - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_loadIncomingEmailFrom(t *testing.T) { - makeBaseConfig := func() (ConfigProvider, ConfigSection) { - cfg, _ := NewConfigProviderFromData("") - sec := cfg.Section("email.incoming") - sec.NewKey("ENABLED", "true") - sec.NewKey("REPLY_TO_ADDRESS", "forge+%{token}@example.com") - - return cfg, sec - } - resetIncomingEmailPort := func() func() { - return func() { - IncomingEmail.Port = 0 - } - } - - t.Run("aliases", func(t *testing.T) { - cfg, sec := makeBaseConfig() - sec.NewKey("USER", "jane.doe@example.com") - sec.NewKey("PASSWD", "y0u'll n3v3r gUess th1S!!1") - - loadIncomingEmailFrom(cfg) - - assert.EqualValues(t, "jane.doe@example.com", IncomingEmail.Username) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) - }) - - t.Run("Port settings", func(t *testing.T) { - t.Run("no port, no tls", func(t *testing.T) { - defer resetIncomingEmailPort()() - cfg, sec := makeBaseConfig() - - // False is the default, but we test it explicitly. - sec.NewKey("USE_TLS", "false") - - loadIncomingEmailFrom(cfg) - - assert.EqualValues(t, 143, IncomingEmail.Port) - }) - - t.Run("no port, with tls", func(t *testing.T) { - defer resetIncomingEmailPort()() - cfg, sec := makeBaseConfig() - - sec.NewKey("USE_TLS", "true") - - loadIncomingEmailFrom(cfg) - - assert.EqualValues(t, 993, IncomingEmail.Port) - }) - - t.Run("port overrides tls", func(t *testing.T) { - defer resetIncomingEmailPort()() - cfg, sec := makeBaseConfig() - - sec.NewKey("PORT", "1993") - sec.NewKey("USE_TLS", "true") - - loadIncomingEmailFrom(cfg) - - assert.EqualValues(t, 1993, IncomingEmail.Port) - }) - }) -} diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index e9ce640c7f..a2bc2df444 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -134,14 +134,6 @@ func loadMailerFrom(rootCfg ConfigProvider) { sec.Key("PROTOCOL").SetValue("smtp+starttls") } - // Handle aliases - if sec.HasKey("USERNAME") && !sec.HasKey("USER") { - sec.Key("USER").SetValue(sec.Key("USERNAME").String()) - } - if sec.HasKey("PASSWORD") && !sec.HasKey("PASSWD") { - sec.Key("PASSWD").SetValue(sec.Key("PASSWORD").String()) - } - // Set default values & validate sec.Key("NAME").MustString(AppName) sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+starttls", "smtp+unix", "sendmail", "dummy"}) diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index f8af4a78c1..fbabf11378 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -38,17 +38,4 @@ func Test_loadMailerFrom(t *testing.T) { assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) }) } - - t.Run("property aliases", func(t *testing.T) { - cfg, _ := NewConfigProviderFromData("") - sec := cfg.Section("mailer") - sec.NewKey("ENABLED", "true") - sec.NewKey("USERNAME", "jane.doe@example.com") - sec.NewKey("PASSWORD", "y0u'll n3v3r gUess th1S!!1") - - loadMailerFrom(cfg) - - assert.EqualValues(t, "jane.doe@example.com", MailService.User) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) - }) } diff --git a/modules/setting/other.go b/modules/setting/other.go index 4ba494765b..706cb1e3d9 100644 --- a/modules/setting/other.go +++ b/modules/setting/other.go @@ -8,7 +8,6 @@ import "code.gitea.io/gitea/modules/log" type OtherConfig struct { ShowFooterVersion bool ShowFooterTemplateLoadTime bool - ShowFooterPoweredBy bool EnableFeed bool EnableSitemap bool } @@ -16,7 +15,6 @@ type OtherConfig struct { var Other = OtherConfig{ ShowFooterVersion: true, ShowFooterTemplateLoadTime: true, - ShowFooterPoweredBy: true, EnableSitemap: true, EnableFeed: true, } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 6086dd1d57..50f0fd704c 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -162,7 +162,7 @@ var ( PreferredLicenses: []string{"Apache-2.0", "MIT"}, DisableHTTPGit: false, AccessControlAllowOrigin: "", - UseCompatSSHURI: true, + UseCompatSSHURI: false, DefaultCloseIssuesViaCommitsInAnyBranch: false, EnablePushCreateUser: false, EnablePushCreateOrg: false, diff --git a/modules/setting/time.go b/modules/setting/time.go index 39acba12ef..6d2aa80f5b 100644 --- a/modules/setting/time.go +++ b/modules/setting/time.go @@ -19,8 +19,9 @@ func loadTimeFrom(rootCfg ConfigProvider) { DefaultUILocation, err = time.LoadLocation(zone) if err != nil { log.Fatal("Load time zone failed: %v", err) + } else { + log.Info("Default UI Location is %v", zone) } - log.Info("Default UI Location is %v", zone) } if DefaultUILocation == nil { DefaultUILocation = time.Local diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index 5b7df127b4..ad86f4ca03 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -30,8 +30,10 @@ type CreateUserOption struct { // EditUserOption edit user options type EditUserOption struct { - SourceID *int64 `json:"source_id"` - LoginName *string `json:"login_name"` + // required: true + SourceID int64 `json:"source_id"` + // required: true + LoginName string `json:"login_name" binding:"Required"` // swagger:strfmt email Email *string `json:"email" binding:"MaxSize(254)"` FullName *string `json:"full_name" binding:"MaxSize(100)"` diff --git a/modules/structs/release.go b/modules/structs/release.go index d8da924f54..c7378645c2 100644 --- a/modules/structs/release.go +++ b/modules/structs/release.go @@ -9,47 +9,43 @@ import ( // Release represents a repository release type Release struct { - ID int64 `json:"id"` - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - TarURL string `json:"tarball_url"` - ZipURL string `json:"zipball_url"` - HideArchiveLinks bool `json:"hide_archive_links"` - UploadURL string `json:"upload_url"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` + ID int64 `json:"id"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + TarURL string `json:"tarball_url"` + ZipURL string `json:"zipball_url"` + UploadURL string `json:"upload_url"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` // swagger:strfmt date-time CreatedAt time.Time `json:"created_at"` // swagger:strfmt date-time - PublishedAt time.Time `json:"published_at"` - Publisher *User `json:"author"` - Attachments []*Attachment `json:"assets"` - ArchiveDownloadCount *TagArchiveDownloadCount `json:"archive_download_count"` + PublishedAt time.Time `json:"published_at"` + Publisher *User `json:"author"` + Attachments []*Attachment `json:"assets"` } // CreateReleaseOption options when creating a release type CreateReleaseOption struct { // required: true - TagName string `json:"tag_name" binding:"Required"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` - HideArchiveLinks bool `json:"hide_archive_links"` + TagName string `json:"tag_name" binding:"Required"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` } // EditReleaseOption options when editing a release type EditReleaseOption struct { - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft *bool `json:"draft"` - IsPrerelease *bool `json:"prerelease"` - HideArchiveLinks *bool `json:"hide_archive_links"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft *bool `json:"draft"` + IsPrerelease *bool `json:"prerelease"` } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 1158160b53..f6cc9803a4 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -89,7 +89,6 @@ type Repository struct { HasWiki bool `json:"has_wiki"` ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` WikiBranch string `json:"wiki_branch,omitempty"` - GloballyEditableWiki bool `json:"globally_editable_wiki"` HasPullRequests bool `json:"has_pull_requests"` HasProjects bool `json:"has_projects"` HasReleases bool `json:"has_releases"` @@ -186,8 +185,6 @@ type EditRepoOption struct { HasWiki *bool `json:"has_wiki,omitempty"` // set this structure to use external wiki instead of internal ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` - // set the globally editable state of the wiki - GloballyEditableWiki *bool `json:"globally_editable_wiki,omitempty"` // sets the default branch for this repository. DefaultBranch *string `json:"default_branch,omitempty"` // sets the branch used for this repository's wiki. diff --git a/modules/structs/repo_compare.go b/modules/structs/repo_compare.go deleted file mode 100644 index 8a12498705..0000000000 --- a/modules/structs/repo_compare.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package structs - -// Compare represents a comparison between two commits. -type Compare struct { - TotalCommits int `json:"total_commits"` // Total number of commits in the comparison. - Commits []*Commit `json:"commits"` // List of commits in the comparison. -} diff --git a/modules/structs/repo_tag.go b/modules/structs/repo_tag.go index 961ca4e53b..4a7d895288 100644 --- a/modules/structs/repo_tag.go +++ b/modules/structs/repo_tag.go @@ -5,25 +5,23 @@ package structs // Tag represents a repository tag type Tag struct { - Name string `json:"name"` - Message string `json:"message"` - ID string `json:"id"` - Commit *CommitMeta `json:"commit"` - ZipballURL string `json:"zipball_url"` - TarballURL string `json:"tarball_url"` - ArchiveDownloadCount *TagArchiveDownloadCount `json:"archive_download_count"` + Name string `json:"name"` + Message string `json:"message"` + ID string `json:"id"` + Commit *CommitMeta `json:"commit"` + ZipballURL string `json:"zipball_url"` + TarballURL string `json:"tarball_url"` } // AnnotatedTag represents an annotated tag type AnnotatedTag struct { - Tag string `json:"tag"` - SHA string `json:"sha"` - URL string `json:"url"` - Message string `json:"message"` - Tagger *CommitUser `json:"tagger"` - Object *AnnotatedTagObject `json:"object"` - Verification *PayloadCommitVerification `json:"verification"` - ArchiveDownloadCount *TagArchiveDownloadCount `json:"archive_download_count"` + Tag string `json:"tag"` + SHA string `json:"sha"` + URL string `json:"url"` + Message string `json:"message"` + Tagger *CommitUser `json:"tagger"` + Object *AnnotatedTagObject `json:"object"` + Verification *PayloadCommitVerification `json:"verification"` } // AnnotatedTagObject contains meta information of the tag object @@ -40,9 +38,3 @@ type CreateTagOption struct { Message string `json:"message"` Target string `json:"target"` } - -// TagArchiveDownloadCount counts how many times a archive was downloaded -type TagArchiveDownloadCount struct { - Zip int64 `json:"zip"` - TarGz int64 `json:"tar_gz"` -} diff --git a/modules/structs/user.go b/modules/structs/user.go index ad529c966e..82b565e5e7 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -19,8 +19,6 @@ type User struct { // the user's authentication sign-in name. // default: empty LoginName string `json:"login_name"` - // The ID of the user's Authentication Source - SourceID int64 `json:"source_id"` // the user's full name FullName string `json:"full_name"` // swagger:strfmt email diff --git a/modules/structs/variable.go b/modules/structs/variable.go deleted file mode 100644 index cc846cf0ec..0000000000 --- a/modules/structs/variable.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package structs - -// CreateVariableOption the option when creating variable -// swagger:model -type CreateVariableOption struct { - // Value of the variable to create - // - // required: true - Value string `json:"value" binding:"Required"` -} - -// UpdateVariableOption the option when updating variable -// swagger:model -type UpdateVariableOption struct { - // New name for the variable. If the field is empty, the variable name won't be updated. - Name string `json:"name"` - // Value of the variable to update - // - // required: true - Value string `json:"value" binding:"Required"` -} - -// ActionVariable return value of the query API -// swagger:model -type ActionVariable struct { - // the owner to which the variable belongs - OwnerID int64 `json:"owner_id"` - // the repository to which the variable belongs - RepoID int64 `json:"repo_id"` - // the name of the variable - Name string `json:"name"` - // the value of the variable - Data string `json:"data"` -} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 2268b8b0fb..c9799a38ec 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -53,13 +53,13 @@ func NewFuncMap() template.FuncMap { "JsonUtils": NewJsonUtils, // ----------------------------------------------------------------- - // svg / avatar / icon / color + // svg / avatar / icon "svg": svg.RenderHTML, "EntryIcon": base.EntryIcon, "MigrationIcon": MigrationIcon, "ActionIcon": ActionIcon, - "SortArrow": SortArrow, - "ContrastColor": util.ContrastColor, + + "SortArrow": SortArrow, // ----------------------------------------------------------------- // time / number / format @@ -109,9 +109,6 @@ func NewFuncMap() template.FuncMap { "ShowFooterTemplateLoadTime": func() bool { return setting.Other.ShowFooterTemplateLoadTime }, - "ShowFooterPoweredBy": func() bool { - return setting.Other.ShowFooterPoweredBy - }, "AllowedReactions": func() []string { return setting.UI.Reactions }, diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 55a55dd7f4..8661653adf 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -138,9 +138,10 @@ func wrapTmplErrMsg(msg string) { if setting.IsProd { // in prod mode, Forgejo must have correct templates to run log.Fatal("Forgejo can't run with template errors: %s", msg) + } else { + // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded + log.Error("There are template errors but Forgejo continues to run in dev mode: %s", msg) } - // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded - log.Error("There are template errors but Forgejo continues to run in dev mode: %s", msg) } type templateErrorPrettier struct { diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index 7c97e1ea89..f1832cba0e 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -84,8 +84,9 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil { if firstRun { log.Fatal("Failed to parse mail template, err: %v", err) + } else { + log.Error("Failed to parse mail template, err: %v", err) } - log.Error("Failed to parse mail template, err: %v", err) } } } diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index c4c5376afd..5446741287 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -135,9 +135,16 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { var ( archivedCSSClass string - textColor = util.ContrastColor(label.Color) + textColor = "#111" labelScope = label.ExclusiveScope() ) + r, g, b := util.HexToRBGColor(label.Color) + + // Determine if label text should be light or dark to be readable on background color + // this doesn't account for saturation or transparency + if util.UseLightTextOnBackground(r, g, b) { + textColor = "#eee" + } description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) @@ -161,7 +168,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m // Make scope and item background colors slightly darker and lighter respectively. // More contrast needed with higher luminance, empirically tweaked. - luminance := util.GetRelativeLuminance(label.Color) + luminance := util.GetLuminance(r, g, b) contrast := 0.01 + luminance*0.03 // Ensure we add the same amount of contrast also near 0 and 1. darken := contrast + math.Max(luminance+contrast-1.0, 0.0) @@ -171,7 +178,6 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0) opacity := GetLabelOpacityByte(label.IsArchived()) - r, g, b := util.HexToRBGColor(label.Color) scopeBytes := []byte{ uint8(math.Min(math.Round(r*darkenFactor), 255)), uint8(math.Min(math.Round(g*darkenFactor), 255)), diff --git a/modules/util/color.go b/modules/util/color.go index 9c520dce78..240b045c28 100644 --- a/modules/util/color.go +++ b/modules/util/color.go @@ -4,10 +4,22 @@ package util import ( "fmt" + "math" "strconv" "strings" ) +// Check similar implementation in web_src/js/utils/color.js and keep synchronization + +// Return R, G, B values defined in reletive luminance +func getLuminanceRGB(channel float64) float64 { + sRGB := channel / 255 + if sRGB <= 0.03928 { + return sRGB / 12.92 + } + return math.Pow((sRGB+0.055)/1.055, 2.4) +} + // Get color as RGB values in 0..255 range from the hex color string (with or without #) func HexToRBGColor(colorString string) (float64, float64, float64) { hexString := colorString @@ -35,23 +47,19 @@ func HexToRBGColor(colorString string) (float64, float64, float64) { return r, g, b } -// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance -// Keep this in sync with web_src/js/utils/color.js -func GetRelativeLuminance(color string) float64 { - r, g, b := HexToRBGColor(color) - return (0.2126729*r + 0.7151522*g + 0.0721750*b) / 255 +// return luminance given RGB channels +// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance +func GetLuminance(r, g, b float64) float64 { + R := getLuminanceRGB(r) + G := getLuminanceRGB(g) + B := getLuminanceRGB(b) + luminance := 0.2126*R + 0.7152*G + 0.0722*B + return luminance } -func UseLightText(backgroundColor string) bool { - return GetRelativeLuminance(backgroundColor) < 0.453 -} - -// Given a background color, returns a black or white foreground color that the highest -// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better. -// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42 -func ContrastColor(backgroundColor string) string { - if UseLightText(backgroundColor) { - return "#fff" - } - return "#000" +// Reference from: https://firsching.ch/github_labels.html +// In the future WCAG 3 APCA may be a better solution. +// Check if text should use light color based on RGB of background +func UseLightTextOnBackground(r, g, b float64) bool { + return GetLuminance(r, g, b) < 0.453 } diff --git a/modules/util/color_test.go b/modules/util/color_test.go index be6e6b122a..d96ac36730 100644 --- a/modules/util/color_test.go +++ b/modules/util/color_test.go @@ -33,31 +33,33 @@ func Test_HexToRBGColor(t *testing.T) { } } -func Test_UseLightText(t *testing.T) { +func Test_UseLightTextOnBackground(t *testing.T) { cases := []struct { - color string - expected string + r float64 + g float64 + b float64 + expected bool }{ - {"#d73a4a", "#fff"}, - {"#0075ca", "#fff"}, - {"#cfd3d7", "#000"}, - {"#a2eeef", "#000"}, - {"#7057ff", "#fff"}, - {"#008672", "#fff"}, - {"#e4e669", "#000"}, - {"#d876e3", "#000"}, - {"#ffffff", "#000"}, - {"#2b8684", "#fff"}, - {"#2b8786", "#fff"}, - {"#2c8786", "#000"}, - {"#3bb6b3", "#000"}, - {"#7c7268", "#fff"}, - {"#7e716c", "#fff"}, - {"#81706d", "#fff"}, - {"#807070", "#fff"}, - {"#84b6eb", "#000"}, + {215, 58, 74, true}, + {0, 117, 202, true}, + {207, 211, 215, false}, + {162, 238, 239, false}, + {112, 87, 255, true}, + {0, 134, 114, true}, + {228, 230, 105, false}, + {216, 118, 227, true}, + {255, 255, 255, false}, + {43, 134, 133, true}, + {43, 135, 134, true}, + {44, 135, 134, true}, + {59, 182, 179, true}, + {124, 114, 104, true}, + {126, 113, 108, true}, + {129, 112, 109, true}, + {128, 112, 112, true}, } for n, c := range cases { - assert.Equal(t, c.expected, ContrastColor(c.color), "case %d: error should match", n) + result := UseLightTextOnBackground(c.r, c.g, c.b) + assert.Equal(t, c.expected, result, "case %d: error should match", n) } } diff --git a/modules/util/util.go b/modules/util/util.go index b6ea283551..e4d658d7f8 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -220,12 +220,3 @@ func Iif[T any](condition bool, trueVal, falseVal T) T { } return falseVal } - -func ReserveLineBreakForTextarea(input string) string { - // Since the content is from a form which is a textarea, the line endings are \r\n. - // It's a standard behavior of HTML. - // But we want to store them as \n like what GitHub does. - // And users are unlikely to really need to keep the \r. - // Other than this, we should respect the original content, even leading or trailing spaces. - return strings.ReplaceAll(input, "\r\n", "\n") -} diff --git a/modules/util/util_test.go b/modules/util/util_test.go index de8f065cad..819e12ee91 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -121,9 +121,9 @@ func Test_NormalizeEOL(t *testing.T) { } func Test_RandomInt(t *testing.T) { - randInt, err := CryptoRandomInt(255) - assert.True(t, randInt >= 0) - assert.True(t, randInt <= 255) + int, err := CryptoRandomInt(255) + assert.True(t, int >= 0) + assert.True(t, int <= 255) assert.NoError(t, err) } @@ -235,8 +235,3 @@ func TestToPointer(t *testing.T) { val123 := 123 assert.False(t, &val123 == ToPointer(val123)) } - -func TestReserveLineBreakForTextarea(t *testing.T) { - assert.Equal(t, ReserveLineBreakForTextarea("test\r\ndata"), "test\ndata") - assert.Equal(t, ReserveLineBreakForTextarea("test\r\ndata\r\n"), "test\ndata\n") -} diff --git a/options/license/AMD-newlib b/options/license/AMD-newlib deleted file mode 100644 index 1b2f1abd6f..0000000000 --- a/options/license/AMD-newlib +++ /dev/null @@ -1,11 +0,0 @@ -Copyright 1989, 1990 Advanced Micro Devices, Inc. - -This software is the property of Advanced Micro Devices, Inc (AMD) which -specifically grants the user the right to modify, use and distribute this -software provided this notice is not removed or altered. All other rights -are reserved by AMD. - -AMD MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH REGARD TO THIS -SOFTWARE. IN NO EVENT SHALL AMD BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL -DAMAGES IN CONNECTION WITH OR ARISING FROM THE FURNISHING, PERFORMANCE, OR -USE OF THIS SOFTWARE. diff --git a/options/license/APL-1.0 b/options/license/APL-1.0 index 0748f90cd9..261f2d687c 100644 --- a/options/license/APL-1.0 +++ b/options/license/APL-1.0 @@ -210,21 +210,21 @@ PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE The Initial Contributor is: ____________________________________________________ - +  [Enter full name of Initial Contributor] Address of Initial Contributor: ________________________________________________ - +  ________________________________________________ - +  ________________________________________________ - +  [Enter address above] The Designated Web Site is: __________________________________________________ - +  [Enter URL for Designated Web Site of Initial Contributor] NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6. @@ -237,27 +237,27 @@ The date on which the Initial Work was first available under this License: _____ PART 3: GOVERNING JURISDICTION -For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here] +For the purposes of this License, the Governing Jurisdiction is _________________________________________________. 
[Initial Contributor to Enter Governing Jurisdiction here] PART 4: THIRD PARTIES For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected. SELECTION - +  BOX PARAGRAPH -[ ] A. "THIRD PARTY" means any third party. - - -[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). - - -[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. - - -[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. - - -[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. +[  ] A. "THIRD PARTY" means any third party. +  +  +[  ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). +  +  +[  ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. +  +  +[  ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. +  +  +[  ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor. PART 5: NOTICE @@ -271,8 +271,8 @@ PART 6: PATENT LICENSING TERMS For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below. Is this a Patents-Included License pursuant to Section 2.2 of the License? -YES [ ] -NO [ ] +YES [      ] +NO [      ] By default, if YES is not selected by the Initial Contributor, the answer is NO. diff --git a/options/license/BSD-2-clause-first-lines b/options/license/BSD-2-clause-first-lines deleted file mode 100644 index 3774caf24a..0000000000 --- a/options/license/BSD-2-clause-first-lines +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone -Corporation). All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer as the first lines of this file unmodified. - -2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/HPND-UC-export-US b/options/license/HPND-UC-export-US deleted file mode 100644 index 015556c5f9..0000000000 --- a/options/license/HPND-UC-export-US +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (C) 1985, 1990 Regents of the University of California. - -Permission to use, copy, modify, and distribute this -software and its documentation for any purpose and without -fee is hereby granted, provided that the above copyright -notice appear in all copies. The University of California -makes no representations about the suitability of this -software for any purpose. It is provided "as is" without -express or implied warranty. Export of this software outside -of the United States of America may require an export license. diff --git a/options/license/IBM-pibs b/options/license/IBM-pibs index ee9c7be36d..49454b8b1e 100644 --- a/options/license/IBM-pibs +++ b/options/license/IBM-pibs @@ -4,5 +4,5 @@ Any user of this software should understand that IBM cannot provide technical su Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software. -COPYRIGHT I B M CORPORATION 2002 -LICENSED MATERIAL - PROGRAM PROPERTY OF I B M +COPYRIGHT   I B M   CORPORATION 2002 +LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M diff --git a/options/license/NCGL-UK-2.0 b/options/license/NCGL-UK-2.0 index 15c4f63c22..31fbad6f83 100644 --- a/options/license/NCGL-UK-2.0 +++ b/options/license/NCGL-UK-2.0 @@ -12,15 +12,15 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application. You are not permitted to: - exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. + exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; If the Information Provider does not provide a specific attribution statement, you must use the following: Contains information licensed under the Non-Commercial Government Licence v2.0. diff --git a/options/license/NCL b/options/license/NCL deleted file mode 100644 index 3bfb658c26..0000000000 --- a/options/license/NCL +++ /dev/null @@ -1,32 +0,0 @@ -Copyright (c) 2004 the University Corporation for Atmospheric -Research ("UCAR"). All rights reserved. Developed by NCAR's -Computational and Information Systems Laboratory, UCAR, -www.cisl.ucar.edu. - -Redistribution and use of the Software in source and binary forms, -with or without modification, is permitted provided that the -following conditions are met: - -- Neither the names of NCAR's Computational and Information Systems -Laboratory, the University Corporation for Atmospheric Research, -nor the names of its sponsors or contributors may be used to -endorse or promote products derived from this Software without -specific prior written permission. - -- Redistributions of source code must retain the above copyright -notices, this list of conditions, and the disclaimer below. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions, and the disclaimer below in the -documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. diff --git a/options/license/NPL-1.1 b/options/license/NPL-1.1 index 0d5457ff04..62c5296400 100644 --- a/options/license/NPL-1.1 +++ b/options/license/NPL-1.1 @@ -2,7 +2,7 @@ Netscape Public LIcense version 1.1 AMENDMENTS -The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. +The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. Additional Terms applicable to the Netscape Public License. @@ -28,7 +28,7 @@ Additional Terms applicable to the Netscape Public License. Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License. EXHIBIT A-Netscape Public License. - +   "The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. @@ -37,8 +37,8 @@ The Original Code is Mozilla Communicator client code, released March 31, 1998. The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved. Contributor(s): ______________________________________. - -Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." +   +Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." Mozilla Public License Version 1.1 diff --git a/options/license/OAR b/options/license/OAR deleted file mode 100644 index ca5c4b9617..0000000000 --- a/options/license/OAR +++ /dev/null @@ -1,12 +0,0 @@ -COPYRIGHT (c) 1989-2013, 2015. -On-Line Applications Research Corporation (OAR). - -Permission to use, copy, modify, and distribute this software for any -purpose without fee is hereby granted, provided that this entire notice -is included in all copies of any software which is or includes a copy -or modification of this software. - -THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED -WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION -OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS -SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. diff --git a/options/license/OCCT-PL b/options/license/OCCT-PL index 9b6fccc1c9..85df3c73c5 100644 --- a/options/license/OCCT-PL +++ b/options/license/OCCT-PL @@ -6,7 +6,7 @@ OPEN CASCADE releases and makes publicly available the source code of the softwa It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software. - +  1. Definitions Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning. @@ -26,13 +26,13 @@ Unless there is something in the subject matter or in the context inconsistent t "Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof. "You" or "Your": means an individual or a legal entity exercising rights under this License - +  2. Acceptance of license By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You. - +  3. Scope and purpose This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License. - +  4. Contributor license Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that: diff --git a/options/license/OGL-UK-1.0 b/options/license/OGL-UK-1.0 index 867c0e353b..a761c9916f 100644 --- a/options/license/OGL-UK-1.0 +++ b/options/license/OGL-UK-1.0 @@ -10,20 +10,20 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; - If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0. + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; +  If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following:
 Contains public sector information licensed under the Open Government Licence v1.0. ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information; ensure that you do not mislead others or misrepresent the Information or its source; ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003. These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically. - Exemptions + Exemptions This licence does not cover the use of: - personal data in the Information; @@ -48,22 +48,22 @@ Definitions In this licence, the terms below have the following meanings: -‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. +‘Information’
means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. -‘Information Provider’ means the person or organisation providing the Information under this licence. +‘Information Provider’
means the person or organisation providing the Information under this licence. -‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. +‘Licensor’
means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. -‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. +‘Use’
as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. -‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. +‘You’
means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. About the Open Government Licence The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence. -The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. +The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so. These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights. -Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. +Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. diff --git a/options/license/OSET-PL-2.1 b/options/license/OSET-PL-2.1 index e0ed2e1398..15f0c7758c 100644 --- a/options/license/OSET-PL-2.1 +++ b/options/license/OSET-PL-2.1 @@ -100,8 +100,7 @@ If it is impossible for You to comply with any of the terms of this License with 5.1 Failure to Comply The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice. - 5.2 Patent Infringement Claims - If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. + 5.2 Patent Infringement Claims
 If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3 Additional Compliance Terms Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years. diff --git a/options/license/SHL-2.0 b/options/license/SHL-2.0 index 9218b47a72..e522a396fe 100644 --- a/options/license/SHL-2.0 +++ b/options/license/SHL-2.0 @@ -1,22 +1,22 @@ # Solderpad Hardware Licence Version 2.0 -This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. +This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. 1. The definitions set out in the Apache License are modified as follows: -Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). +Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). -Contribution also includes any design, as well as any work of authorship. +Contribution also includes any design, as well as any work of authorship. -Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. +Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. -Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). +Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). -Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). +Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). -Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. -Work also includes a design or work of authorship, whether in Source form or other Object form. +Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. +Work also includes a design or work of authorship, whether in Source form or other Object form. 2. Grant of Licence -2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. +2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. diff --git a/options/license/SHL-2.1 b/options/license/SHL-2.1 index c9ae53741f..4815a9e5ed 100644 --- a/options/license/SHL-2.1 +++ b/options/license/SHL-2.1 @@ -19,7 +19,7 @@ The following definitions shall replace the corresponding definitions in the Apa "License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License. - +  "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). diff --git a/options/license/SISSL b/options/license/SISSL index af38d02d92..7d6ad9d66c 100644 --- a/options/license/SISSL +++ b/options/license/SISSL @@ -36,13 +36,13 @@ Sun Industry Standards Source License - Version 1.1 2.0 SOURCE CODE LICENSE - 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:  (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. - (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications. + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.  3.0 DISTRIBUTION OBLIGATIONS @@ -92,14 +92,14 @@ This License represents the complete agreement concerning subject matter hereof. EXHIBIT A - Sun Standards License -"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. +"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either  express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. -The Initial Developer of the Original Code is: +The Initial Developer of the Original Code is:  Sun Microsystems, Inc.. Portions created by: _______________________________________ diff --git a/options/license/Sun-PPP-2000 b/options/license/Sun-PPP-2000 deleted file mode 100644 index 914c19544a..0000000000 --- a/options/license/Sun-PPP-2000 +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2000 by Sun Microsystems, Inc. -All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation is hereby granted, provided that the above copyright -notice appears in all copies. - -SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF -THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR -ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR -DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES diff --git a/options/license/W3C-19980720 b/options/license/W3C-19980720 index 134879044d..a8554039ef 100644 --- a/options/license/W3C-19980720 +++ b/options/license/W3C-19980720 @@ -4,7 +4,7 @@ Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of T This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: -Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: +Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: 1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. diff --git a/options/license/pkgconf b/options/license/pkgconf deleted file mode 100644 index b8b2ffd996..0000000000 --- a/options/license/pkgconf +++ /dev/null @@ -1,7 +0,0 @@ -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -This software is provided 'as is' and without any warranty, express or -implied. In no event shall the authors be liable for any damages arising -from the use of this software. diff --git a/options/license/xzoom b/options/license/xzoom deleted file mode 100644 index f312dedbc2..0000000000 --- a/options/license/xzoom +++ /dev/null @@ -1,12 +0,0 @@ -Copyright Itai Nahshon 1995, 1996. -This program is distributed with no warranty. - -Source files for this program may be distributed freely. -Modifications to this file are okay as long as: - a. This copyright notice and comment are preserved and - left at the top of the file. - b. The man page is fixed to reflect the change. - c. The author of this change adds his name and change - description to the list of changes below. -Executable files may be distributed with sources, or with -exact location where the source code can be obtained. diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index bcd4b6af9f..133e84a6e1 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -138,7 +138,7 @@ reinstall_confirm_message = إعادة التثبيت باستخدام قاعد db_schema = مخطط reinstall_error = أنت تحاول التثبيت في قاعدة بيانات فورجيو موجودة sqlite_helper = مسار المِلَفّ لقاعدة بيانات SQLite3.
    أدخل المسار المطلق إذا شغلت فورجيو كخدمة. -require_db_desc = فورجيو يحتاج MySQL أو PostgreSQL أو SQLite3 أو TiDB. +require_db_desc = فورجيو يحتاج MySQL أو PostgreSQL أو MSSQL أو SQLite3 أو TiDB. password = كلمة المرور host = المضيف docker_helper = إذا كنت تستخدم فورجيو داخل دوكر، يرجي قراءة المستندات قبل تغيير أي إعدادات. @@ -1548,6 +1548,7 @@ dashboard.sync_tag.started = بدأ تزامن الوسوم dashboard.sync_repo_tags = زامن الوسوم من بيانات جِت إلى قاعدة البيانات self_check = فحص ذاتي self_check.database_collation_case_insensitive = تستخدم قاعدة البيانات تجميع %s ، وهو تجميع غير حساس. على الرغم من أن فورجيو يمكن أن يعمل معها قد تكون هناك حالات نادرة لا تعمل كما هو متوقع. +self_check.database_fix_mssql = بالنسبة لمستخدمي الـإم إس سيكول، يمكنك إصلاح المشكلة بتعديل السيكول يدوياً فقط في الوقت الراهن. monitor.process.cancel_desc = قد يسبب إلغاء العملية فقدانًا للبيانات monitor.queue.type = النوع monitor.process.cancel_notices = أتريد إلغاء: %s؟ diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index d7093b85d5..7042b42e4a 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -1242,7 +1242,7 @@ admin_name = Потреб. име за администратор confirm_password = Потвърдете паролата title = Първоначална конфигурация domain = Домейн на сървъра -require_db_desc = Forgejo изисква MySQL, PostgreSQL, SQLite3 или TiDB (MySQL протокол). +require_db_desc = Forgejo изисква MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (MySQL протокол). general_title = Общи настройки email_title = Настройки на ел. поща db_schema = Схема diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 49aabeaf05..4f0f435d69 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -220,7 +220,7 @@ license_desc=Vyzkoušejte dokumentaci. -require_db_desc=Forgejo vyžaduje MySQL, PostgreSQL, SQLite3 nebo TiDB (protokol MySQL). +require_db_desc=Forgejo vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 nebo TiDB (protokol MySQL). db_title=Nastavení databáze db_type=Typ databáze host=Hostitel @@ -3426,6 +3426,7 @@ self_check = Vlastní kontrola dashboard.sync_tag.started = Synchronizace značek spuštěna dashboard.rebuild_issue_indexer = Přestavit indexer vydání self_check.database_collation_case_insensitive = Databáze používá collation %s. Jedná se o intenzivní collation. Ačkoli s ní Forgejo nejspíše bude pracovat, mohou nastat určité vzácné případy, kdy nebude pracovat tak, jak má. +self_check.database_fix_mssql = Uživatelé MSSQL mohou tento problém vyřešit pouze ručními SQL příkazy „ALTER ... COLLATE ...“. auths.oauth2_map_group_to_team = Zmapovat zabrané skupiny u týmů organizací (volitelné - vyžaduje název claimu výše) monitor.queue.settings.desc = Pooly dynamicky rostou podle blokování fronty jejich workerů. @@ -3434,6 +3435,7 @@ self_check.database_collation_mismatch=Očekávejte, že databáze použije coll self_check.database_collation_case_insensitive=Databáze používá collation %s, což je collation nerozlišující velká a malá písmena. Ačkoli s ní Gitea může pracovat, mohou se vyskytnout vzácné případy, kdy nebude fungovat podle očekávání. self_check.database_inconsistent_collation_columns=Databáze používá collation %s, ale tyto sloupce používají chybné collation. To může způsobit neočekávané problémy. self_check.database_fix_mysql=Pro uživatele MySQL/MariaDB můžete použít příkaz "gitea doctor convert", který opraví problémy s collation, nebo můžete také problém vyřešit příkazem "ALTER ... COLLATE ..." SQL ručně. +self_check.database_fix_mssql=Uživatelé MSSQL mohou problém vyřešit pouze pomocí příkazu "ALTER ... COLLATE ..." SQL ručně. auths.tips.gmail_settings = Nastavení služby Gmail: config_summary = Souhrn config.open_with_editor_app_help = Editory v nabídce „Otevřít pomocí“ v nabídce klonování. Ponechte prázdné pro použití výchozího editoru (zobrazíte jej rozšířením). diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 07ea493409..2a5ab7f44c 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -218,7 +218,7 @@ license_desc=Hole dir Dokumentation, bevor du irgendwelche Einstellungen veränderst. -require_db_desc=Forgejo benötigt MySQL, PostgreSQL, SQLite3 oder TiDB (MySQL-Protokoll). +require_db_desc=Forgejo benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll). db_title=Datenbankeinstellungen db_type=Datenbanktyp host=Host @@ -3398,6 +3398,7 @@ self_check = Selbstprüfung dashboard.sync_repo_tags = Tags aus Git-Daten zu Datenbank synchronisieren emails.change_email_text = Bist du dir sicher, dass du diese E-Mail-Addresse aktualisieren möchtest? packages.cleanup.success = Abgelaufene Daten erfolgreich gesäubert +self_check.database_fix_mssql = Für MSSQL-Benutzer: Du kannst das Problem im Moment nur mit „ALTER … COLLATE …“-SQLs beheben. self_check.no_problem_found = Noch kein Problem gefunden. self_check.database_inconsistent_collation_columns = Datenbank benutzt Collation %s, doch diese Spalten verwenden Collations, die nicht zusammenpassen. Das könnte ein paar unerwartete Probleme verursachen. self_check.database_collation_mismatch = Erwarte von Datenbank, folgende Collation zu verwenden: %s diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 0be1aab87f..30383665ad 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -217,7 +217,7 @@ license_desc=Κατέβασε το εγχειρίδιο πριν αλλάξετε τις ρυθμίσεις. -require_db_desc=Το Forgejo απαιτεί MySQL, PostgreSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL). +require_db_desc=Το Forgejo απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL). db_title=Ρυθμίσεις βάσης δεδομένων db_type=Είδος βάσης δεδομένων host=Διακομιστής @@ -3385,6 +3385,7 @@ notices.desc=Περιγραφή notices.op=Λειτ. notices.delete_success=Οι ειδοποιήσεις του συστήματος έχουν διαγραφεί. self_check.no_problem_found = Μέχρι τώρα, δεν έχει βρεθεί κάποιο πρόβλημα. +self_check.database_fix_mssql = Προς το παρόν, οι χρήστες του MSSQL μπορούν να διορθώσουν το πρόβλημα αυτό χειροκίνητα χρησιμοποιώντας τις εντολές SQL «ALTER ... COLLATE ...». self_check = Αυτοέλεγχος dashboard.sync_repo_tags = Συγχρονισμός tag, χρησιμοποιώντας τα δεδομένα git στην βάση δεδομένων dashboard.sync_tag.started = Ο συγχρονισμός tag έχει ξεκινήσει diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9148d57a27..919f38ad68 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -239,7 +239,7 @@ license_desc = Go get documentation before changing any settings. -require_db_desc = Forgejo requires MySQL, PostgreSQL, SQLite3 or TiDB (MySQL protocol). +require_db_desc = Forgejo requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title = Database settings db_type = Database type host = Host @@ -562,7 +562,6 @@ TeamName = Team name AuthName = Authorization name AdminEmail = Admin email To = Branch name -AccessToken = Access token NewBranchName = New branch name CommitSummary = Commit summary @@ -2375,7 +2374,6 @@ settings.slack_token = Token settings.slack_domain = Domain settings.slack_channel = Channel settings.add_web_hook_desc = Integrate %s into your repository. -settings.graphql_url = GraphQL URL settings.web_hook_name_gitea = Gitea settings.web_hook_name_forgejo = Forgejo settings.web_hook_name_gogs = Gogs @@ -2395,10 +2393,10 @@ settings.packagist_api_token = API token settings.packagist_package_url = Packagist package URL settings.web_hook_name_sourcehut_builds = SourceHut Builds settings.sourcehut_builds.manifest_path = Build manifest path +settings.sourcehut_builds.graphql_url = GraphQL URL (e.g. https://builds.sr.ht/query) settings.sourcehut_builds.visibility = Job visibility settings.sourcehut_builds.secrets = Secrets settings.sourcehut_builds.secrets_helper = Give the job access to the build secrets (requires the SECRETS:RO grant) -settings.sourcehut_builds.access_token_helper = Access token that has JOBS:RW grant. Generate a builds.sr.ht token or a builds.sr.ht token with secrets access on meta.sr.ht. settings.deploy_keys = Deploy keys settings.add_deploy_key = Add deploy key settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. @@ -2505,8 +2503,6 @@ settings.thread_id = Thread ID settings.matrix.homeserver_url = Homeserver URL settings.matrix.room_id = Room ID settings.matrix.message_type = Message type -settings.matrix.access_token_helper = It is recommended to setup a dedicated Matrix account for this. The access token can be retrieved from the Element web client (in a private/incognito tab) > User menu (top left) > All settings > Help & About > Advanced > Access Token (right below the Homeserver URL). Close the private/incognito tab (logging out would invalidate the token). -settings.matrix.room_id_helper = The Room ID can be retrieved from the Element web client > Room Settings > Advanced > Internal room ID. Example: %s. settings.archive.button = Archive repo settings.archive.header = Archive this repo settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. @@ -2657,8 +2653,6 @@ release.downloads = Downloads release.download_count_one = %s download release.download_count_few = %s downloads release.add_tag_msg = Use the title and content of release as tag message. -release.hide_archive_links = Hide automatically generated archives -release.hide_archive_links_helper = Hide automatically generated source code archives for this release. For example, if you are uploading your own. release.add_tag = Create tag release.releases_for = Releases for %s release.tags_for = Tags for %s @@ -3095,7 +3089,6 @@ auths.attribute_mail = Email attribute auths.attribute_ssh_public_key = Public SSH key attribute auths.attribute_avatar = Avatar attribute auths.attributes_in_bind = Fetch attributes in bind DN context -auths.default_domain_name = Default domain name used for the email address auths.allow_deactivate_all = Allow an empty search result to deactivate all users auths.use_paged_search = Use paged search auths.search_page_size = Page size @@ -3389,6 +3382,7 @@ self_check.database_collation_mismatch = Expect database to use collation: %s self_check.database_collation_case_insensitive = Database is using a collation %s, which is an insensitive collation. Although Forgejo could work with it, there might be some rare cases which don't work as expected. self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. It might cause some unexpected problems. self_check.database_fix_mysql = For MySQL/MariaDB users, you could use the "gitea doctor convert" command to fix the collation problems, or you could also fix the problem by "ALTER ... COLLATE ..." SQLs manually. +self_check.database_fix_mssql = For MSSQL users, you could only fix the problem by "ALTER ... COLLATE ..." SQLs manually at the moment. [action] create_repo = created repository %s diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 02542631eb..8796d9c86b 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -226,7 +226,7 @@ disable_gravatar = Malŝalti profilbildojn per Gravatar repo_path_helper = Foraj Git-deponejoj konserviĝos al tiu ĉi dosierujo. sqlite_helper = Dosiervojo por la datumbazo SQLite3.
    Enigu absolutan vojon se vi rulas Forgejon kiel servo. enable_captcha = Ŝalti dumregistriĝan teston de homeco -require_db_desc = Forgejo bezonas kiel datumbazo MySQL, PostgreSQL, SQLite3, aŭ TiDB (MySQL komunikformo). +require_db_desc = Forgejo bezonas kiel datumbazo MySQL, PostgreSQL, MSSQL, SQLite3, aŭ TiDB (MySQL komunikformo). smtp_from = Sendu retleterojn kiel general_title = Ĝeneralaj agordoj password = Pasvorto diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 7727e930a6..3f189a9c46 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -210,7 +210,7 @@ license_desc=¡Está todo en < documentación antes de realizar cambios en la configuración. -require_db_desc=Forgejo requiere una base de datos MySQL, PostgreSQL, SQLite3 o TiDB (usar el protocolo MySQL). +require_db_desc=Forgejo requiere una base de datos MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (usar el protocolo MySQL). db_title=Configuración de base de datos db_type=Tipo de base de datos host=Servidor diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 1560075d6b..1bb1cd5da6 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -170,7 +170,7 @@ license_desc=Mene osoitteeseen ohjeet ennen minkään asetuksen muuttamista. -require_db_desc=Forgejo tarvitsee toimiakseen MySQL, PostgreSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan. +require_db_desc=Forgejo tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan. db_title=Tietokanta asetukset db_type=Tietokanta tyyppi host=Isäntä diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 45430fe366..881fd2c4d4 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -212,7 +212,7 @@ reinstall_error = Sinusubukan mong mag-install sa umiiral na Forgejo database install = Pag-install title = Paunang pagsasaayos docker_helper = Kapag tinatakbo mo ang Forgejo sa loob ng Docker, mangyaring basahin ang dokumentasyon bago baguhin ang anumang mga setting. -require_db_desc = Kinakailangan ng Forgejo ang MySQL, PostgreSQL, SQLite3 o TiDB (MySQL protocol). +require_db_desc = Kinakailangan ng Forgejo ang MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (MySQL protocol). db_title = Mga setting ng database db_type = Uri ng database host = Host diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 4ffccbca44..9fe60d9ff4 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -219,7 +219,7 @@ license_desc=Toutes les sources sont sur documentation avant de modifier les paramètres. -require_db_desc=Forgejo nécessite MySQL, PostgreSQL, SQLite3 ou TiDB (avec le protocole MySQL). +require_db_desc=Forgejo nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL). db_title=Paramètres de la base de données db_type=Type de base de données host=Hôte @@ -3422,12 +3422,14 @@ self_check.database_collation_mismatch = La base de donnée devrait utiliser la self_check.database_collation_case_insensitive = La base de donnée utilise la collation %s qui n'est pas sensible à la casse. Bien que Forgejo puisse fonctionner de cette façon, il est possible que certains cas limite d'utilisation de la casse ne fonctionne pas comme attendu. self_check.database_inconsistent_collation_columns = La base de donnée utilise la collation %s, mais ces colonnes utilisent des collations incohérentes. Cela peut causer des problèmes inattendus. self_check.database_fix_mysql = Les utilisateurs de MySQL/MariaDB peuvent utiliser la commande "forgejo doctor convert" pour corriger les problèmes de collation, ou bien manuellement avec la commande SQL "ALTER ... COLLATE ...". +self_check.database_fix_mssql = Les utilisateurs de MSSQL sont pour l'instant contraint d'utiliser la commande SQL "ALTER ... COLLATE ..." pour corriger ce problème. self_check.no_problem_found=Aucun problème trouvé pour l’instant. self_check.database_collation_mismatch=Exige que la base de données utilise la collation %s. self_check.database_collation_case_insensitive=La base de données utilise la collation %s, insensible à la casse. Bien que Forgejo soit compatible, il peut y avoir quelques rares cas qui ne fonctionnent pas comme prévu. self_check.database_inconsistent_collation_columns=La base de données utilise la collation %s, mais ces colonnes utilisent des collations différentes. Cela peut causer des problèmes imprévus. self_check.database_fix_mysql=Pour les utilisateurs de MySQL ou MariaDB, vous pouvez utiliser la commande « forgejo doctor convert » dans un terminal ou exécuter une requête du type « ALTER … COLLATE ... » pour résoudre les problèmes de collation. +self_check.database_fix_mssql=Pour les utilisateurs de MSSQL, vous ne pouvez résoudre le problème qu’en exécutant une requête SQL du type « ALTER … COLLATE … ». config_settings = Paramètres config_summary = Résumé auths.tips.gmail_settings = Paramètres Gmail : @@ -3845,4 +3847,4 @@ b = o [markup] filepreview.line = Ligne %[1]d dans %[2]s filepreview.lines = Lignes %[1]d jusqu'à %[2]d dans %[3]s -filepreview.truncated = L'aperçu a été tronqué \ No newline at end of file +filepreview.truncated = L'aperçu a été tronqué diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index e448ee4568..3948015902 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -141,7 +141,7 @@ license_desc=Sæktu leiðbeiningaritið áður en þú breytir stillingum. -require_db_desc=Forgejo krefst MySQL, PostgreSQL, SQLite3 eða TiDB (MySQL samskiptareglur). +require_db_desc=Forgejo krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur). db_title=Gagnagrunnsstillingar db_type=Tegund Gagnagrunns host=Hýsill diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 2af6019eae..caa4569164 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -217,7 +217,7 @@ install_desc = Semplicemente la documentazione prima di cambiare qualsiasi impostazione. -require_db_desc=Forgejo requires MySQL, PostgreSQL, SQLite3 or TiDB (MySQL protocol). +require_db_desc=Forgejo requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title=Impostazioni database db_type=Tipo di database host=Host @@ -3404,6 +3404,7 @@ monitor.queue.settings.remove_all_items_done = Tutti gli elementi in coda sono s self_check.database_collation_mismatch = Pretendi che la base di dati usi la collazione: %s self_check.database_fix_mysql = Per utenti MySQL/MariaDB, potresti usare il comando "gitea doctor convert" per risolvere problemi di collazione, o potresti risolvere il problema manualmente tramite SQL con "ALTER ... COLLATE ...". self_check.database_collation_case_insensitive = La base di dati sta usando la collazione %s, che è una collazione insensibile. Nonostante Forgejo potrebbe lavorarci, ci potrebbero essere rari casi che non vanno come previsto. +self_check.database_fix_mssql = Gli utenti MSSQL possono provare a risolvere il problema tramite SQL con "ALTER ... COLLATE ..." manualmente, per il momento. auths.tip.gitlab_new = Registra una nuova applicazione su https://gitlab.com/-/profile/applications config_summary = Riepilogo config.open_with_editor_app_help = L'editor delle opzioni "Apri con" per il menu di clone. Se lasciato vuoto, verranno usati i default. Espandi per vedere i default. @@ -3811,4 +3812,4 @@ b = B [markup] filepreview.lines = Linee da %[1]d a %[2]d in %[3]s filepreview.truncated = L'anteprima è stata troncata -filepreview.line = Linea %[1]d in %[2]s \ No newline at end of file +filepreview.line = Linea %[1]d in %[2]s diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index d6137f39fe..35c78fd3c1 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -217,7 +217,7 @@ license_desc=Go get ドキュメントを読んでください。 -require_db_desc=Forgejoには、MySQL、PostgreSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。 +require_db_desc=Forgejoには、MySQL、PostgreSQL、MSSQL、SQLite3、またはTiDB(MySQL プロトコル) が必要です。 db_title=データベース設定 db_type=データベースのタイプ host=ホスト diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index e2ab79e3f8..53e168bff1 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -197,7 +197,7 @@ license_desc=Iegūsti vadlīninas pirms maināt iestatījumus. -require_db_desc=Forgejo nepieciešams MySQL, PostgreSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu). +require_db_desc=Forgejo nepieciešams MySQL, PostgreSQL, MSSQL, SQLite3 vai TiDB (izmantojot MySQL protokolu). db_title=Datu bāzes iestatījumi db_type=Datu bāzes veids host=Resursdators diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index bdb6cd50d6..f380a1129d 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -217,7 +217,7 @@ install_desc = Draai gewoon documentatie voordat je een instelling aanpast. -require_db_desc=Forgejo vereist MySQL, PostgreSQL, SQLite3 of TiDB (MySQL protocol). +require_db_desc=Forgejo vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol). db_title=Database-instellingen db_type=Database-type host=Server @@ -3333,6 +3333,7 @@ dashboard.sync_repo_branches = Synchroniseren gemiste branches van git data naar monitor.processes_count = %d Processen monitor.process.children = Kinderen self_check.database_inconsistent_collation_columns = Database gebruikt collatie %s, maar deze kolommen gebruiken onjuiste collaties. Dit kan onverwachte problemen veroorzaken. +self_check.database_fix_mssql = Voor MSSQL gebruikers kan je het probleem alleen oplossen door "ALTER ... COLLATE ..." SQL's handmatig op te lossen. monitor.stacktrace = Stacktrace monitor.download_diagnosis_report = Diagnoserapport downloaden self_check.database_collation_case_insensitive = Database gebruikt collatie %s, wat een ongevoelige collatie is. Hoewel Forgejo ermee kan werken, kunnen er enkele zeldzame gevallen zijn die niet werken zoals verwacht. diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 38109eb2c3..6774dc2167 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -212,7 +212,7 @@ install_desc = Po prostu dokumentację przed wprowadzeniem jakichkolwiek zmian. -require_db_desc=Forgejo wymaga MySQL, PostgreSQL, SQLite3 lub TiDB (protokół MySQL). +require_db_desc=Forgejo wymaga MySQL, PostgreSQL, MSSQL, SQLite3 lub TiDB (protokół MySQL). db_title=Ustawienia bazy danych db_type=Typ bazy danych host=Serwer diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index ce752bbddc..bc0edd51fd 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -210,7 +210,7 @@ install_desc = Apenas documentação cuidadosamente antes de alterar qualquer coisa nesta página. -require_db_desc=Forgejo requer MySQL, PostgreSQL, SQLite3 ou TiDB (protocolo MySQL). +require_db_desc=Forgejo requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). db_title=Configurações de banco de dados db_type=Tipo de banco de dados host=Servidor diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index c7fc74b234..ced06fb5f7 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -218,7 +218,7 @@ license_desc=Vá buscá-lo em documentação antes de alterar quaisquer configurações. -require_db_desc=Forgejo requer MySQL, PostgreSQL, SQLite3 ou TiDB (protocolo MySQL). +require_db_desc=Forgejo requer MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (protocolo MySQL). db_title=Configurações da base de dados db_type=Tipo de base de dados host=Servidor @@ -3405,6 +3405,7 @@ self_check.database_collation_mismatch=Supor que a base de dados usa a colação self_check.database_collation_case_insensitive=A base de dados está a usar a colação %s, que é insensível à diferença entre maiúsculas e minúsculas. Embora o Gitea possa trabalhar com ela, pode haver alguns casos raros que não funcionem como esperado. self_check.database_inconsistent_collation_columns=A base de dados está a usar a colação %s, mas estas colunas estão a usar colações diferentes. Isso poderá causar alguns problemas inesperados. self_check.database_fix_mysql=Para utilizadores do MySQL/MariaDB, pode usar o comando "gitea doctor convert" para resolver os problemas de colação. Também pode resolver o problema com comandos SQL "ALTER ... COLLATE ..." aplicados manualmente. +self_check.database_fix_mssql=Para utilizadores do MSSQL só pode resolver o problema aplicando comandos SQL "ALTER ... COLLATE ..." manualmente, por enquanto. config_summary = Resumo auths.tips.gmail_settings = Configurações do Gmail: config_settings = Configurações @@ -3813,4 +3814,4 @@ b = B [markup] filepreview.lines = Linhas %[1]d até %[2]d em %[3]s filepreview.line = Linha %[1]d em %[2]s -filepreview.truncated = A previsão foi truncada \ No newline at end of file +filepreview.truncated = A previsão foi truncada diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 7ba9719a05..14dd189dfa 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -218,7 +218,7 @@ license_desc=Всё это на документацией, прежде чем изменять любые настройки. -require_db_desc=Forgejo требуется MySQL, PostgreSQL, SQLite3 или TiDB (по протоколу MySQL). +require_db_desc=Forgejo требуется MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (по протоколу MySQL). db_title=Настройки базы данных db_type=Тип базы данных host=Хост @@ -3384,6 +3384,7 @@ notices.delete_success=Уведомления системы были удале self_check.no_problem_found = Пока проблем не обнаружено. auths.tip.gitea = Зарегистрируйте новое приложение OAuth2. Доступна инструкция: https://forgejo.org/docs/latest/user/oauth2-provider auths.tips.oauth2.general.tip = При регистрации нового приложения OAuth2 ссылка обратного перенаправления должна быть: +self_check.database_fix_mssql = В настоящий момент пользователи MSSQL могут исправить проблемы с сопоставлением только ручным прописыванием "ALTER ... COLLATE ..." в SQL. self_check.database_fix_mysql = Пользователи MySQL и MariaDB могут исправить проблемы с сопоставлением командой "gitea doctor convert". Также можно вручную вписать "ALTER ... COLLATE ..." в SQL. dashboard.cleanup_actions = Очистить устаревшие журналы и артефакты Действий dashboard.sync_repo_branches = Синхронизировать ветки из Git в базу данных diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 2167d116d0..39fa7fef2f 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -196,7 +196,7 @@ license_desc=Získajte dokumentáciu pred zmenou akýchkoľvek nastavení. -require_db_desc=Forgejo vyžaduje MySQL, PostgreSQL, SQLite3 alebo TiDB (MySQL protokol). +require_db_desc=Forgejo vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol). db_title=Nastavenie databázy db_type=Typ databázy host=Host diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index ed6153b70f..c3b1dc6ba5 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -133,7 +133,7 @@ pull_requests = Zahteve za umik [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. -require_db_desc = Forgejo zahteva MySQL, PostgreSQL, SQLite3 ali TiDB (protokol MySQL). +require_db_desc = Forgejo zahteva MySQL, PostgreSQL, MSSQL, SQLite3 ali TiDB (protokol MySQL). password_algorithm_helper = Nastavite algoritem za stiskanje gesla. Algoritmi imajo različne zahteve in moč. Algoritem argon2 je precej varen, vendar porabi veliko pomnilnika in je lahko neprimeren za majhne sisteme. reinstall_confirm_message = Ponovna namestitev z obstoječo zbirko podatkov Forgejo lahko povzroči več težav. V večini primerov morate za zagon programa Forgejo uporabiti obstoječi "app.ini". Če veste, kaj počnete, potrdite naslednje: err_admin_name_is_reserved = Administrator Uporabniško ime je neveljavno, uporabniško ime je rezervirano diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index c2c5385421..e9da230fea 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -212,7 +212,7 @@ license_desc=Gidin ve belgeleri okuyun. -require_db_desc=Forgejo MySQL, PostgreSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir. +require_db_desc=Forgejo MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir. db_title=Veritabanı Ayarları db_type=Veritabanı Türü host=Sunucu diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index dab4287f06..05447378e2 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -218,7 +218,7 @@ license_desc=所有的代码都开源在 官方文档 后再对本页面进行填写。 -require_db_desc=Forgejo 需要使用 MySQL、PostgreSQL、SQLite3 或 TiDB(MySQL 协议)等数据库。 +require_db_desc=Forgejo 需要使用 MySQL、PostgreSQL、MSSQL、SQLite3 或 TiDB(MySQL 协议)等数据库。 db_title=数据库设置 db_type=数据库类型 host=数据库主机 @@ -3410,12 +3410,14 @@ self_check.database_collation_mismatch = 期望数据库使用排序规则:%s self_check.database_collation_case_insensitive = 数据库正在使用 %s 排序规则,这是一种不敏感的排序规则。 尽管 Forgejo 可以使用它,但在极少数情况下可能无法按照预期工作。 self_check.database_inconsistent_collation_columns = 数据库正在使用 %s 排序规则,但与这些列使用的排序规则不匹配。 这可能会导致一些意想不到的问题。 self_check.database_fix_mysql = 对于 MySQL/MariaDB 用户,您可以使用 "gitea doctor convert" 命令来修复排序规则问题,也可以通过SQL命令 "ALTER ... COLLATE ..." 来手动修复问题。 +self_check.database_fix_mssql = 对于 MSSQL 用户,目前您只能通过SQL命令 "ALTER ... COLLATE ..." 来手动修复问题。 self_check.no_problem_found=尚未发现问题。 self_check.database_collation_mismatch=期望数据库使用的校验方式:%s self_check.database_collation_case_insensitive=数据库正在使用一个校验 %s, 这是一个不敏感的校验. 虽然Gitea可以与它合作,但可能有一些罕见的情况不如预期的那样起作用。 self_check.database_inconsistent_collation_columns=数据库正在使用%s的排序规则,但是这些列使用了不匹配的排序规则。这可能会造成一些意外问题。 self_check.database_fix_mysql=对于MySQL/MariaDB用户,您可以使用“gitea doctor convert”命令来解决校验问题。 或者您也可以通过 "ALTER ... COLLATE ..." 这样的SQL 来手动解决这个问题。 +self_check.database_fix_mssql=对于MSSQL用户,您现在只能通过"ALTER ... COLLATE ..."SQLs手动解决这个问题。 auths.tips.gmail_settings = Gmail 设置: auths.tip.gitlab_new = 在 https://gitlab.com/-/profile/applications 上注册新应用 config_settings = 设置 @@ -3825,4 +3827,4 @@ mib = MiB [markup] filepreview.line = %[2]s 中的第 %[1]d 行 filepreview.lines = %[3]s 中的第 %[1]d 到 %[2]d 行 -filepreview.truncated = 预览已被截断 \ No newline at end of file +filepreview.truncated = 预览已被截断 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index e7334dbd16..82109204ea 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -217,7 +217,7 @@ license_desc=取得 安裝指南再來調整設定。 -require_db_desc=Forgejo 需要 MySQL、PostgreSQL、SQLite3、TiDB (MySQL 協定) 等其中一項。 +require_db_desc=Forgejo 需要 MySQL、PostgreSQL、SQLite3、MSSQL、TiDB (MySQL 協定) 等其中一項。 db_title=資料庫設定 db_type=資料庫類型 host=主機 diff --git a/package-lock.json b/package-lock.json index 5781507561..dff43d2da7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,10 +5,11 @@ "packages": { "": { "dependencies": { - "@citation-js/core": "0.7.11", - "@citation-js/plugin-bibtex": "0.7.11", - "@citation-js/plugin-csl": "0.7.11", + "@citation-js/core": "0.7.9", + "@citation-js/plugin-bibtex": "0.7.9", + "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", + "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", "@github/text-expander-element": "2.6.1", @@ -20,43 +21,42 @@ "chart.js": "4.4.2", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.0.1", - "clippie": "4.1.1", - "css-loader": "7.0.0", - "dayjs": "1.11.11", + "clippie": "4.0.7", + "css-loader": "6.10.0", + "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.12", + "htmx.org": "1.9.11", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.10", "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.9.0", - "mini-css-extract-plugin": "2.9.0", - "minimatch": "9.0.4", + "mini-css-extract-plugin": "2.8.1", + "minimatch": "9.0.3", "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.38", "postcss-loader": "8.1.1", - "postcss-nesting": "12.1.2", + "postcss-nesting": "12.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.17.2", - "tailwindcss": "3.4.3", - "temporal-polyfill": "0.2.4", + "swagger-ui-dist": "5.12.0", + "tailwindcss": "3.4.1", + "temporal-polyfill": "0.2.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vanilla-colorful": "0.7.2", - "vue": "3.4.26", + "vue": "3.4.21", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.1", + "vue-chartjs": "5.3.0", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -64,11 +64,11 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", - "@playwright/test": "1.43.0", - "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.8.0", - "@stylistic/stylelint-plugin": "2.1.2", + "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", + "@playwright/test": "1.42.1", + "@stoplight/spectral-cli": "6.11.0", + "@stylistic/eslint-plugin-js": "1.7.0", + "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", "eslint": "8.57.0", @@ -78,25 +78,24 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.5.0", - "eslint-plugin-sonarjs": "0.25.1", - "eslint-plugin-unicorn": "52.0.0", - "eslint-plugin-vitest": "0.5.4", + "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-sonarjs": "0.24.0", + "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-vitest": "0.4.0", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.25.0", + "eslint-plugin-vue": "9.24.0", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.1.0", - "happy-dom": "14.7.1", - "markdownlint-cli": "0.40.0", + "eslint-plugin-wc": "2.0.4", + "happy-dom": "14.3.10", + "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", - "stylelint": "16.4.0", + "stylelint": "16.3.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", - "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", - "updates": "16.0.1", - "vite-string-plugin": "1.3.1", - "vitest": "1.5.3" + "updates": "16.0.0", + "vite-string-plugin": "1.1.5", + "vitest": "1.4.0" }, "engines": { "node": ">= 18.0.0" @@ -235,9 +234,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -246,9 +245,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", - "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -262,9 +261,9 @@ "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" }, "node_modules/@citation-js/core": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.11.tgz", - "integrity": "sha512-evQtyzeW+Gbmq+xWciIq9sbcvXXDbm8q32orD/HDd5ay6RQFKoW/BKxBLp+Nmpxgspb9sxTJn3iFK7+jxOTNTw==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.9.tgz", + "integrity": "sha512-fSbkB32JayDChZnAYC/kB+sWHRvxxL7ibVetyBOyzOc+5aCnjb6UVsbcfhnkOIEyAMoRRvWDyFmakEoTtA5ttQ==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -292,9 +291,9 @@ } }, "node_modules/@citation-js/plugin-bibtex": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.11.tgz", - "integrity": "sha512-G4vEmLjrQUxgBIp3ffWN5dDOlwjPsrRSi/uTyxDJuFgKBD8GR1eO7Y/ZcePNAOHMqUxG7lxhhBbZJwcJZNVHYw==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.9.tgz", + "integrity": "sha512-gIJpCd6vmmTOcRfDrSOjtoNhw2Mi94UwFxmgJ7GwkXyTYcNheW5VlMMo1tlqjakJGARQ0eOsKcI57gSPqJSS2g==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -320,9 +319,9 @@ } }, "node_modules/@citation-js/plugin-csl": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.11.tgz", - "integrity": "sha512-4OGZ9wHZDfpgiPU2cOXWGuKt7P+ndGWAeLG95nOG+DXe5U+f9EEZTXfaM4C99x8Ri+g6JklR96A3kuYZxYLllg==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.9.tgz", + "integrity": "sha512-mbD7CnUiPOuVnjeJwo+d0RGUcY0PE8n01gHyjq0qpTeS42EGmQ9+LzqfsTUVWWBndTwc6zLRuIF1qFAUHKE4oA==", "dependencies": { "@citation-js/date": "^0.5.0", "citeproc": "^2.4.6" @@ -396,6 +395,14 @@ "node": ">=14.0.0" } }, + "node_modules/@claviska/jquery-minicolors": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@claviska/jquery-minicolors/-/jquery-minicolors-2.3.6.tgz", + "integrity": "sha512-8Ro6D4GCrmOl41+6w4NFhEOpx8vjxwVRI69bulXsFDt49uVRKhLU5TnzEV7AmOJrylkVq+ugnYNMiGHBieeKUQ==", + "peerDependencies": { + "jquery": ">= 1.7.x" + } + }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz", @@ -482,9 +489,9 @@ } }, "node_modules/@csstools/selector-specificity": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.3.tgz", - "integrity": "sha512-KEPNw4+WW5AVEIyzC80rTbWEUatTW2lXpN8+8ILC8PiPeWPjwUzrPZDIOZ2wwqDmeqOYTdSGyL3+vE5GC3FB3Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz", + "integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==", "funding": [ { "type": "github", @@ -866,9 +873,9 @@ } }, "node_modules/@eslint-community/eslint-plugin-eslint-comments": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz", - "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.1.0.tgz", + "integrity": "sha512-B2mwipifrBS5E00vN8vME68laPMZ0h3sNGOEDj5g9iUN9k5EU99Omq0Nc325eKNoFFDnDtiHp3DqIjO+1bstag==", "dev": true, "dependencies": { "escape-string-regexp": "^4.0.0", @@ -878,7 +885,7 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1060,9 +1067,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1351,12 +1358,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.0.tgz", - "integrity": "sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", "dev": true, "dependencies": { - "playwright": "1.43.0" + "playwright": "1.42.1" }, "bin": { "playwright": "cli.js" @@ -1427,9 +1434,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", "cpu": [ "arm" ], @@ -1440,9 +1447,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", "cpu": [ "arm64" ], @@ -1453,9 +1460,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", "cpu": [ "arm64" ], @@ -1466,9 +1473,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", "cpu": [ "x64" ], @@ -1479,9 +1486,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", "cpu": [ "arm" ], @@ -1492,9 +1499,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", "cpu": [ "arm64" ], @@ -1505,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", "cpu": [ "arm64" ], @@ -1517,23 +1524,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", - "cpu": [ - "ppc64le" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", "cpu": [ "riscv64" ], @@ -1543,23 +1537,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", "cpu": [ "x64" ], @@ -1570,9 +1551,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", "cpu": [ "x64" ], @@ -1583,9 +1564,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", "cpu": [ "arm64" ], @@ -1596,9 +1577,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", "cpu": [ "ia32" ], @@ -1609,9 +1590,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", "cpu": [ "x64" ], @@ -1719,9 +1700,9 @@ } }, "node_modules/@stoplight/spectral-cli": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.11.1.tgz", - "integrity": "sha512-1zqsQ0TOuVSnxxZ9mHBfC0IygV6ex7nAY6Mp59mLmw5fW103U9yPVK5ZcX9ZngCmr3PdteAnMDUIIaoDGso6nA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.11.0.tgz", + "integrity": "sha512-IURDN47BPIf3q4ZyUPujGpBzuHWFE5yT34w9rTJ1GKA4SgdscEdQO9KoTjOPT4G4cvDlEV3bNxwQ3uRm7+wRlA==", "dev": true, "dependencies": { "@stoplight/json": "~3.21.0", @@ -1742,7 +1723,7 @@ "pony-cause": "^1.0.0", "stacktracey": "^2.1.7", "tslib": "^2.3.0", - "yargs": "~17.7.2" + "yargs": "17.3.1" }, "bin": { "spectral": "dist/index.js" @@ -1906,33 +1887,20 @@ } }, "node_modules/@stoplight/spectral-parsers": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.4.tgz", - "integrity": "sha512-nCTVvtX6q71M8o5Uvv9kxU31Gk1TRmgD6/k8HBhdCmKG6FWcwgjiZouA/R3xHLn/VwTI/9k8SdG5Mkdy0RBqbQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.3.tgz", + "integrity": "sha512-J0KW5Rh5cHWnJQ3yN+cr/ijNFVirPSR0pkQbdrNX30VboEl083UEDrQ3yov9kjLVIWEk9t9kKE7Eo3QT/k4JLA==", "dev": true, "dependencies": { "@stoplight/json": "~3.21.0", - "@stoplight/types": "^14.1.1", - "@stoplight/yaml": "~4.3.0", + "@stoplight/types": "^13.6.0", + "@stoplight/yaml": "~4.2.3", "tslib": "^2.3.1" }, "engines": { "node": "^12.20 || >=14.13" } }, - "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", - "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, "node_modules/@stoplight/spectral-ref-resolver": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.4.tgz", @@ -2001,27 +1969,6 @@ "node": ">=12" } }, - "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", - "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", - "dev": true, - "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.1", - "@stoplight/types": "^13.0.0", - "@stoplight/yaml-ast-parser": "0.0.48", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=10.8" - } - }, - "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml-ast-parser": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", - "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==", - "dev": true - }, "node_modules/@stoplight/spectral-rulesets": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.18.1.tgz", @@ -2092,14 +2039,14 @@ } }, "node_modules/@stoplight/yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", - "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", + "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", "dev": true, "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.5", - "@stoplight/types": "^14.1.1", - "@stoplight/yaml-ast-parser": "0.0.50", + "@stoplight/ordered-object-literal": "^1.0.1", + "@stoplight/types": "^13.0.0", + "@stoplight/yaml-ast-parser": "0.0.48", "tslib": "^2.2.0" }, "engines": { @@ -2107,31 +2054,18 @@ } }, "node_modules/@stoplight/yaml-ast-parser": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", - "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==", + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", + "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==", "dev": true }, - "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", - "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } - }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.8.0.tgz", - "integrity": "sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz", + "integrity": "sha512-PN6On/+or63FGnhhMKSQfYcWutRlzOiYlVdLM6yN7lquoBTqUJHYnl4TA4MHwiAt46X5gRxDr1+xPZ1lOLcL+Q==", "dev": true, "dependencies": { - "@types/eslint": "^8.56.10", + "@types/eslint": "^8.56.2", "acorn": "^8.11.3", "escape-string-regexp": "^4.0.0", "eslint-visitor-keys": "^3.4.3", @@ -2145,19 +2079,19 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.2.tgz", - "integrity": "sha512-JsSqu0Y3vsX+PBl+DwULxC0cIv9C1yIcq1MXkx7pBOGtTqU26a75I8MPYMiEYvrsXgsKLi65xVgy1iLVSZquJA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.0.tgz", + "integrity": "sha512-mUZEW9uImHSbXeyzbFmHb8WPBv56UTaEnWL/3dGdAiJ54C+8GTfDwDVdI6gbqT9wV7zynkPu7tCXc5746H9mZQ==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4", - "@csstools/media-query-list-parser": "^2.1.9", + "@csstools/css-parser-algorithms": "^2.5.0", + "@csstools/css-tokenizer": "^2.2.3", + "@csstools/media-query-list-parser": "^2.1.7", "is-plain-object": "^5.0.0", - "postcss-selector-parser": "^6.0.16", + "postcss-selector-parser": "^6.0.15", "postcss-value-parser": "^4.2.0", "style-search": "^0.1.0", - "stylelint": "^16.4.0" + "stylelint": "^16.2.1" }, "engines": { "node": "^18.12 || >=20.9" @@ -2224,9 +2158,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "version": "8.56.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.6.tgz", + "integrity": "sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2276,9 +2210,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", "dependencies": { "undici-types": "~5.26.4" } @@ -2321,16 +2255,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", + "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/type-utils": "7.4.0", + "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2356,15 +2290,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", + "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4" }, "engines": { @@ -2384,13 +2318,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", + "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2401,13 +2335,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", + "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.4.0", + "@typescript-eslint/utils": "7.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2428,9 +2362,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", + "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2441,13 +2375,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", + "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/visitor-keys": "7.4.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2468,33 +2402,18 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", + "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/scope-manager": "7.4.0", + "@typescript-eslint/types": "7.4.0", + "@typescript-eslint/typescript-estree": "7.4.0", "semver": "^7.5.4" }, "engines": { @@ -2509,12 +2428,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", + "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/types": "7.4.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2545,13 +2464,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", - "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", + "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", "chai": "^4.3.10" }, "funding": { @@ -2559,12 +2478,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", - "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", + "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.3", + "@vitest/utils": "1.4.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2600,9 +2519,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", - "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", + "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2614,9 +2533,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", - "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2626,9 +2545,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", - "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", + "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2638,9 +2557,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", - "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2668,102 +2587,105 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz", - "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", + "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/shared": "3.4.26", + "@babel/parser": "^7.23.9", + "@vue/shared": "3.4.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz", - "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", + "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", "dependencies": { - "@vue/compiler-core": "3.4.26", - "@vue/shared": "3.4.26" + "@vue/compiler-core": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz", - "integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", + "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/compiler-core": "3.4.26", - "@vue/compiler-dom": "3.4.26", - "@vue/compiler-ssr": "3.4.26", - "@vue/shared": "3.4.26", + "@babel/parser": "^7.23.9", + "@vue/compiler-core": "3.4.21", + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21", "estree-walker": "^2.0.2", - "magic-string": "^0.30.10", - "postcss": "^8.4.38", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.7", + "postcss": "^8.4.35", + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz", - "integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", + "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", "dependencies": { - "@vue/compiler-dom": "3.4.26", - "@vue/shared": "3.4.26" + "@vue/compiler-dom": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/reactivity": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.26.tgz", - "integrity": "sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", + "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", "dependencies": { - "@vue/shared": "3.4.26" + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.26.tgz", - "integrity": "sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", + "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", "dependencies": { - "@vue/reactivity": "3.4.26", - "@vue/shared": "3.4.26" + "@vue/reactivity": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz", - "integrity": "sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", + "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", "dependencies": { - "@vue/runtime-core": "3.4.26", - "@vue/shared": "3.4.26", + "@vue/runtime-core": "3.4.21", + "@vue/shared": "3.4.21", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.26.tgz", - "integrity": "sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", + "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", "dependencies": { - "@vue/compiler-ssr": "3.4.26", - "@vue/shared": "3.4.26" + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { - "vue": "3.4.26" + "vue": "3.4.21" } }, "node_modules/@vue/shared": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz", - "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==" + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, "node_modules/@vue/test-utils": { "version": "2.4.5", @@ -3589,9 +3511,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", "funding": [ { "type": "opencollective", @@ -3779,9 +3701,9 @@ } }, "node_modules/clippie": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.1.tgz", - "integrity": "sha512-D9OOW77Kkj9YEiDXTQjZJZLvTjJPEmK2IBx8JbGJIZaqVd8RvSvxwIN4KVSEFQfu9Jh0z5FL6Pdc4SIknllFFA==" + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.0.7.tgz", + "integrity": "sha512-xmIARCRFQUoCR0kNNu4uIv5f/IFqM1fUts0vQwt1hQEdCPEqs3/dTaG38WenlWOgs3Fcn73PBYXbPIVSlOgFRw==" }, "node_modules/cliui": { "version": "7.0.4", @@ -3990,30 +3912,30 @@ } }, "node_modules/css-functions-list": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", - "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz", + "integrity": "sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==", "dev": true, "engines": { "node": ">=12 || >=16" } }, "node_modules/css-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.0.0.tgz", - "integrity": "sha512-WrO4FVoamxt5zY9CauZjoJgXRi/LZKIk+Ta7YvpSGr5r/eMYPNp5/T9ODlMe4/1rF5DYlycG1avhV4g3A/tiAw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz", + "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.5.4" }, "engines": { - "node": ">= 18.12.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", @@ -4021,7 +3943,7 @@ }, "peerDependencies": { "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { "@rspack/core": { @@ -4631,9 +4553,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", - "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -4938,9 +4860,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.727.tgz", - "integrity": "sha512-brpv4KTeC4g0Fx2FeIKytLd4UGn1zBQq5Lauy7zEWT9oqkaj5mgsxblEZIAOf1HHLlXxzr6adGViiBy5Z39/CA==" + "version": "1.4.716", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.716.tgz", + "integrity": "sha512-t/MXMzFKQC3UfMDpw7V5wdB/UAB8dWx4hEsy+fpPYJWW3gqh3u5T1uXp6vR+H6dGCPBxkRo+YBcapBLvbGQHRw==" }, "node_modules/elkjs": { "version": "0.9.2", @@ -5011,9 +4933,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", + "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -5055,11 +4977,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", + "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", + "typed-array-length": "^1.0.5", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -5770,9 +5692,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.5.0.tgz", - "integrity": "sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.4.0.tgz", + "integrity": "sha512-OL2S6VPjQhs9s/NclQ0qattVq1J0GU8ox70/HIVy5Dxw+qbbdd7KQkyucsez2clEQjvdtDe12DTnPphFFUyXFg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -5791,9 +5713,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz", - "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.24.0.tgz", + "integrity": "sha512-87zp50mbbNrSTuoEOebdRQBPa0mdejA5UEjyuScyIw8hEpEjfWP89Qhkq5xVZfVyVSRQKZc9alVm7yRKQvvUmg==", "dev": true, "engines": { "node": ">=16" @@ -5803,9 +5725,9 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "52.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", - "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", + "version": "51.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", + "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -5836,18 +5758,18 @@ } }, "node_modules/eslint-plugin-vitest": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", - "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.0.tgz", + "integrity": "sha512-3oWgZIwdWVBQ5plvkmOBjreIGLQRdYb7x54OP8uIRHeZyRVJIdOn9o/qWVb9292fDMC8jn7H7d9TSFBZqhrykQ==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^7.7.1" + "@typescript-eslint/utils": "^7.2.0" }, "engines": { "node": "^18.0.0 || >= 20.0.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": ">=8.0.0", "vitest": "*" }, "peerDependenciesMeta": { @@ -5865,110 +5787,10 @@ "integrity": "sha512-ZSsVOaOIig0oVLzRTyk8lUfBfqzWxr/J3/NFMfGGRIkGQPejJYmDH3gXmSJxAojts77uzAGB/UmVrwi2DC4LYA==", "dev": true }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", - "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", - "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", - "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz", - "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "semver": "^7.6.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", - "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.7.1", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/eslint-plugin-vue": { - "version": "9.25.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz", - "integrity": "sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz", + "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -5984,7 +5806,7 @@ "node": "^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/eslint-plugin-vue-scoped-css": { @@ -6014,9 +5836,9 @@ } }, "node_modules/eslint-plugin-wc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz", - "integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.0.4.tgz", + "integrity": "sha512-ORu7MBv0hXIvq894EJad70m+AvHGbmrDdKT6lcgtCVVhEbuIAyxg0ilfqqqHOmsh8PfcUBeEae3y7CElKvm1KQ==", "dev": true, "dependencies": { "is-valid-element-name": "^1.0.0", @@ -6775,9 +6597,9 @@ } }, "node_modules/happy-dom": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.7.1.tgz", - "integrity": "sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==", + "version": "14.3.10", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.3.10.tgz", + "integrity": "sha512-Rh5li9vA9MF9Gkg85CbFABKTa3uoSAByILRNGb92u/vswDd561gBg2p1UW1ZauvDWWwRxPcbACK5zv3BR+gHnQ==", "dev": true, "dependencies": { "entities": "^4.5.0", @@ -6924,9 +6746,9 @@ } }, "node_modules/htmx.org": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz", - "integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw==" + "version": "1.9.11", + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz", + "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw==" }, "node_modules/human-signals": { "version": "5.0.0", @@ -8212,9 +8034,9 @@ } }, "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", + "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", "dev": true, "dependencies": { "argparse": "^2.0.1", @@ -8222,20 +8044,20 @@ "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" + "uc.micro": "^2.0.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdownlint": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", - "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.33.0.tgz", + "integrity": "sha512-4lbtT14A3m0LPX1WS/3d1m7Blg+ZwiLq36WvjQqFGsX3Gik99NV+VXp/PW3n+Q62xyPdbvGOCfjPqjW+/SKMig==", "dev": true, "dependencies": { - "markdown-it": "14.1.0", - "markdownlint-micromark": "0.1.9" + "markdown-it": "14.0.0", + "markdownlint-micromark": "0.1.8" }, "engines": { "node": ">=18" @@ -8245,22 +8067,20 @@ } }, "node_modules/markdownlint-cli": { - "version": "0.40.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz", - "integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.39.0.tgz", + "integrity": "sha512-ZuFN7Xpsbn1Nbp0YYkeLOfXOMOfLQBik2lKRy8pVI/llmKQ2uW7x+8k5OMgF6o7XCsTDSYC/OOmeJ+3qplvnJQ==", "dev": true, "dependencies": { - "commander": "~12.0.0", + "commander": "~11.1.0", "get-stdin": "~9.0.0", - "glob": "~10.3.12", - "ignore": "~5.3.1", + "glob": "~10.3.10", + "ignore": "~5.3.0", "js-yaml": "^4.1.0", "jsonc-parser": "~3.2.1", - "jsonpointer": "5.0.1", - "markdownlint": "~0.34.0", - "minimatch": "~9.0.4", - "run-con": "~1.3.2", - "toml": "~3.0.0" + "markdownlint": "~0.33.0", + "minimatch": "~9.0.3", + "run-con": "~1.3.2" }, "bin": { "markdownlint": "markdownlint.js" @@ -8270,25 +8090,25 @@ } }, "node_modules/markdownlint-cli/node_modules/commander": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", - "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { - "node": ">=18" + "node": ">=16" } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -8307,12 +8127,12 @@ "dev": true }, "node_modules/markdownlint-micromark": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", - "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.8.tgz", + "integrity": "sha512-1ouYkMRo9/6gou9gObuMDnvZM8jC/ly3QCFQyoSPCS2XV1ZClU0xpKbL1Ar3bWWRT1RnBZkWUEiNKrI2CwiBQA==", "dev": true, "engines": { - "node": ">=18" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/DavidAnson" @@ -8912,9 +8732,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -8931,9 +8751,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -9631,12 +9451,12 @@ "dev": true }, "node_modules/playwright": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", - "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", "dev": true, "dependencies": { - "playwright-core": "1.43.0" + "playwright-core": "1.42.1" }, "bin": { "playwright": "cli.js" @@ -9649,9 +9469,9 @@ } }, "node_modules/playwright-core": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", - "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -9794,9 +9614,9 @@ } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -9805,9 +9625,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", + "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -9821,9 +9641,9 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", + "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -9867,9 +9687,9 @@ } }, "node_modules/postcss-nesting": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.2.tgz", - "integrity": "sha512-FUmTHGDNundodutB4PUBxt/EPuhgtpk8FJGRsBhOuy+6FnkR2A8RZWIsyyy6XmhvX2DZQQWIkvu+HB4IbJm+Ew==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz", + "integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==", "funding": [ { "type": "github", @@ -9882,7 +9702,7 @@ ], "dependencies": { "@csstools/selector-resolve-nested": "^1.1.0", - "@csstools/selector-specificity": "^3.0.3", + "@csstools/selector-specificity": "^3.0.2", "postcss-selector-parser": "^6.0.13" }, "engines": { @@ -11109,23 +10929,17 @@ } }, "node_modules/strip-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", - "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", "dev": true, "dependencies": { - "js-tokens": "^9.0.0" + "js-tokens": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", - "dev": true - }, "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", @@ -11133,20 +10947,20 @@ "dev": true }, "node_modules/stylelint": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.4.0.tgz", - "integrity": "sha512-uSx7VMuXwLuYcNSIg+0/fFNv0WinsfLAqsVVy7h7p80clKOHiGE8pfY6UjqwylTHiJrRIahTl6a8FPxGezhWoA==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.0.tgz", + "integrity": "sha512-hqC6xNTbQ5HRGQXfIW4HwXcx09raIFz4W4XFbraeqWqYRVVY/ibYvI0dsu0ORMQY8re2bpDdCAeIa2cm+QJ4Sw==", "dev": true, "dependencies": { "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", "@csstools/media-query-list-parser": "^2.1.9", - "@csstools/selector-specificity": "^3.0.3", + "@csstools/selector-specificity": "^3.0.2", "@dual-bundle/import-meta-resolve": "^4.0.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.2", + "css-functions-list": "^3.2.1", "css-tree": "^2.3.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", @@ -11175,7 +10989,7 @@ "strip-ansi": "^7.1.0", "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.8.2", + "table": "^6.8.1", "write-file-atomic": "^5.0.1" }, "bin": { @@ -11213,22 +11027,6 @@ "stylelint": ">=7 <=16" } }, - "node_modules/stylelint-value-no-unknown-custom-properties": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/stylelint-value-no-unknown-custom-properties/-/stylelint-value-no-unknown-custom-properties-6.0.1.tgz", - "integrity": "sha512-N60PTdaTknB35j6D4FhW0GL2LlBRV++bRpXMMldWMQZ240yFQaoltzlLY4lXXs7Z0J5mNUYZQ/gjyVtU2DhCMA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0", - "resolve": "^1.22.8" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": ">=16" - } - }, "node_modules/stylelint/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -11386,15 +11184,15 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -11497,9 +11295,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.17.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.2.tgz", - "integrity": "sha512-V/NqUw6QoTrjSpctp2oLQvxrl3vW29UsUtZyq7B1CF0v870KOFbYGDQw8rpKaKm0JxTwHpWnW1SN9YuKZdiCyw==" + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz", + "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ==" }, "node_modules/sync-fetch": { "version": "0.4.5", @@ -11530,9 +11328,9 @@ } }, "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -11546,9 +11344,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -11558,7 +11356,7 @@ "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", + "jiti": "^1.19.1", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -11635,22 +11433,22 @@ } }, "node_modules/temporal-polyfill": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.4.tgz", - "integrity": "sha512-WA5p0CjQTkMjF9m8sP4wSYgpqI8m2d4q7wPUyaJOWhy4bI9mReLb2yGvTV4qf/DPMTe6H6M/Dig5KmTMB7ev6Q==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.3.tgz", + "integrity": "sha512-7ZJRc7wq/1XjrOQYkkNpgo2qfE9XLrUU8D/DS+LAC/T0bYqZ46rW6dow0sOTXTPZS4bwer8bD/0OyuKQBfA3yw==", "dependencies": { - "temporal-spec": "^0.2.4" + "temporal-spec": "^0.2.0" } }, "node_modules/temporal-spec": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz", - "integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.0.tgz", + "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ==" }, "node_modules/terser": { - "version": "5.30.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz", - "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -11833,12 +11631,6 @@ "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz", "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==" }, - "node_modules/toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "dev": true - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -12011,9 +11803,9 @@ } }, "node_modules/typescript": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", - "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "devOptional": true, "peer": true, "bin": { @@ -12117,9 +11909,9 @@ } }, "node_modules/updates": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.1.tgz", - "integrity": "sha512-If3NQKzGcA3aVgz2VyOXqQ+4uqYjPUPqh2PeZPtD+OKT4CTmxRYqoyFO+T3nwfccy4SiWy5AabWrBXXhVQ89Aw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/updates/-/updates-16.0.0.tgz", + "integrity": "sha512-Ra3QUu/rfbSCsG83zNNvoRQt0FVT3qULBSALYTlwTDX6oyz7R5GQAYwqJoIG/RDjYAXpwr3usrInoyHHTP6cag==", "dev": true, "bin": { "updates": "dist/updates.js" @@ -12204,19 +11996,14 @@ "builtins": "^1.0.3" } }, - "node_modules/vanilla-colorful": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz", - "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==" - }, "node_modules/vite": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", - "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz", + "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", - "postcss": "^8.4.38", + "postcss": "^8.4.36", "rollup": "^4.13.0" }, "bin": { @@ -12265,9 +12052,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", - "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", + "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -12287,9 +12074,9 @@ } }, "node_modules/vite-string-plugin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.1.tgz", - "integrity": "sha512-0Wu9yNw4QlSVM4SlwozzxR0geMoKFrAIpMldgPuzDvV8lWT1v+0pFXYt+t48qocYXBaxiuVRE3qcsEwFDHBAmA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.5.tgz", + "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==", "dev": true }, "node_modules/vite/node_modules/@types/estree": { @@ -12313,9 +12100,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -12328,35 +12115,33 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, "node_modules/vitest": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", - "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", + "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.3", - "@vitest/runner": "1.5.3", - "@vitest/snapshot": "1.5.3", - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/expect": "1.4.0", + "@vitest/runner": "1.4.0", + "@vitest/snapshot": "1.4.0", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -12368,9 +12153,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.3", + "tinypool": "^0.8.2", "vite": "^5.0.0", - "vite-node": "1.5.3", + "vite-node": "1.4.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -12385,8 +12170,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.3", - "@vitest/ui": "1.5.3", + "@vitest/browser": "1.4.0", + "@vitest/ui": "1.4.0", "happy-dom": "*", "jsdom": "*" }, @@ -12412,24 +12197,27 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" } }, "node_modules/vue": { - "version": "3.4.26", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.26.tgz", - "integrity": "sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", + "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", "dependencies": { - "@vue/compiler-dom": "3.4.26", - "@vue/compiler-sfc": "3.4.26", - "@vue/runtime-dom": "3.4.26", - "@vue/server-renderer": "3.4.26", - "@vue/shared": "3.4.26" + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-sfc": "3.4.21", + "@vue/runtime-dom": "3.4.21", + "@vue/server-renderer": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { "typescript": "*" @@ -12450,9 +12238,9 @@ } }, "node_modules/vue-chartjs": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz", - "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz", + "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==", "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" @@ -13025,18 +12813,18 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "dependencies": { - "cliui": "^8.0.1", + "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^21.0.0" }, "engines": { "node": ">=12" @@ -13051,37 +12839,6 @@ "node": ">=12" } }, - "node_modules/yargs/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index b380a33568..b50c52cf43 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "node": ">= 18.0.0" }, "dependencies": { - "@citation-js/core": "0.7.11", - "@citation-js/plugin-bibtex": "0.7.11", - "@citation-js/plugin-csl": "0.7.11", + "@citation-js/core": "0.7.9", + "@citation-js/plugin-bibtex": "0.7.9", + "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", + "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", "@github/text-expander-element": "2.6.1", @@ -19,43 +20,42 @@ "chart.js": "4.4.2", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.0.1", - "clippie": "4.1.1", - "css-loader": "7.0.0", - "dayjs": "1.11.11", + "clippie": "4.0.7", + "css-loader": "6.10.0", + "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.12", + "htmx.org": "1.9.11", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.10", "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.9.0", - "mini-css-extract-plugin": "2.9.0", - "minimatch": "9.0.4", + "mini-css-extract-plugin": "2.8.1", + "minimatch": "9.0.3", "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.38", "postcss-loader": "8.1.1", - "postcss-nesting": "12.1.2", + "postcss-nesting": "12.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.17.2", - "tailwindcss": "3.4.3", - "temporal-polyfill": "0.2.4", + "swagger-ui-dist": "5.12.0", + "tailwindcss": "3.4.1", + "temporal-polyfill": "0.2.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vanilla-colorful": "0.7.2", - "vue": "3.4.26", + "vue": "3.4.21", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.1", + "vue-chartjs": "5.3.0", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -63,11 +63,11 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", - "@playwright/test": "1.43.0", - "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.8.0", - "@stylistic/stylelint-plugin": "2.1.2", + "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", + "@playwright/test": "1.42.1", + "@stoplight/spectral-cli": "6.11.0", + "@stylistic/eslint-plugin-js": "1.7.0", + "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", "eslint": "8.57.0", @@ -77,25 +77,26 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.5.0", - "eslint-plugin-sonarjs": "0.25.1", - "eslint-plugin-unicorn": "52.0.0", - "eslint-plugin-vitest": "0.5.4", + "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-sonarjs": "0.24.0", + "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-vitest": "0.4.0", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.25.0", + "eslint-plugin-vue": "9.24.0", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.1.0", - "happy-dom": "14.7.1", - "markdownlint-cli": "0.40.0", + "eslint-plugin-wc": "2.0.4", + "happy-dom": "14.3.10", + "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", - "stylelint": "16.4.0", + "stylelint": "16.3.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", - "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", - "updates": "16.0.1", - "vite-string-plugin": "1.3.1", - "vitest": "1.5.3" + "updates": "16.0.0", + "vite-string-plugin": "1.1.5", + "vitest": "1.4.0" }, - "browserslist": ["defaults"] + "browserslist": [ + "defaults" + ] } diff --git a/release-notes/8.0.0/3139.md b/release-notes/8.0.0/3139.md deleted file mode 100644 index cac054bee8..0000000000 --- a/release-notes/8.0.0/3139.md +++ /dev/null @@ -1 +0,0 @@ -Allow hiding auto generated release archives diff --git a/release-notes/8.0.0/3383.md b/release-notes/8.0.0/3383.md deleted file mode 100644 index 9832030aa3..0000000000 --- a/release-notes/8.0.0/3383.md +++ /dev/null @@ -1 +0,0 @@ -The default config for `database.MAX_OPEN_CONNS` changed from 0 (unlimited) to 100 to avoid problems if it exceeds the limit by the database server. If you require high concurrency, try to increase this value for both Forgejo **and your database server**. [`Limit database max connections by default`](https://codeberg.org/forgejo/forgejo/pulls/3383) diff --git a/release-notes/8.0.0/3414.md b/release-notes/8.0.0/3414.md deleted file mode 100644 index 2e10483e23..0000000000 --- a/release-notes/8.0.0/3414.md +++ /dev/null @@ -1 +0,0 @@ -Allow to customize the domain name used as a fallback when synchronizing sources from ldap [`ldap: default domain name`](https://codeberg.org/forgejo/forgejo/pulls/3414) diff --git a/release-notes/8.0.0/feat/3307.md b/release-notes/8.0.0/feat/3307.md deleted file mode 100644 index 6d7dd01415..0000000000 --- a/release-notes/8.0.0/feat/3307.md +++ /dev/null @@ -1 +0,0 @@ -Support [Proof Key for Code Exchange (PKCE - RFC7636)](https://www.rfc-editor.org/rfc/rfc7636) for external login sources using the OAuth2 flow. diff --git a/release-notes/8.0.0/feat/3434.md b/release-notes/8.0.0/feat/3434.md deleted file mode 100644 index a8b28eb1c3..0000000000 --- a/release-notes/8.0.0/feat/3434.md +++ /dev/null @@ -1 +0,0 @@ -When PDFs are displayed in the repository, the [full height of the screen](https://codeberg.org/forgejo/forgejo/pulls/3434) is now used instead of a predefined fixed height diff --git a/release-notes/8.0.0/feat/3615.md b/release-notes/8.0.0/feat/3615.md deleted file mode 100644 index f2dd891c95..0000000000 --- a/release-notes/8.0.0/feat/3615.md +++ /dev/null @@ -1,5 +0,0 @@ -Support the [Nix tarball fetcher immutable link protocol](https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md) on archive URLs, so Forgejo-generated tarballs for branches will go into Nix's `flake.lock` as their respective commit URLs and `nix flake update` will just work. This allows natively fetching Forgejo repositories for Nix flake inputs as tarballs rather than as Git repositories, significantly improving fetch times and avoiding depending on Git at runtime. - -Concretely, Forgejo now returns a header of the following format from its archive URLs: `Link: rel="immutable"`. - -Example usage: `inputs.meow.url = "https://my-forgejo/someuser/somerepo/archive/main.tar.gz";` in `flake.nix`. For a private repository, configure `netrc-file` in `nix.conf` and use `https://my-forgejo/api/v1/repos/someuser/somerepo/archive/main.tar.gz` as a URL instead, since the normal archive endpoint doesn't support tokens. diff --git a/release-notes/8.0.0/fix/3363.md b/release-notes/8.0.0/fix/3363.md deleted file mode 100644 index 65b516cabc..0000000000 --- a/release-notes/8.0.0/fix/3363.md +++ /dev/null @@ -1,6 +0,0 @@ -Reverted the rootless container image path in `GITEA_APP_INI` from -`/etc/gitea/app.ini` to its default value of -`/var/lib/gitea/custom/conf/app.ini`. This allows container users to not have -to mount two separate volumes (one for the configuration data and one for the -configuration `.ini` file). A warning is issued for users with the legacy -configuration on how to update to the new path. diff --git a/release-notes/8.0.0/fix/3430.md b/release-notes/8.0.0/fix/3430.md deleted file mode 100644 index 17d91653e9..0000000000 --- a/release-notes/8.0.0/fix/3430.md +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug where the `/api/v1/repos/{owner}/{repo}/wiki` API endpoints were using a hardcoded "master" branch for the wiki, rather than the branch they really use. diff --git a/release-notes/8.0.0/fix/3442.md b/release-notes/8.0.0/fix/3442.md deleted file mode 100644 index 7c4feafb68..0000000000 --- a/release-notes/8.0.0/fix/3442.md +++ /dev/null @@ -1 +0,0 @@ -Save updated empty comments instead of skipping the update silently, [which prevented the removal of attachments of such comments](https://codeberg.org/forgejo/forgejo/issues/3424). diff --git a/renovate.json b/renovate.json index 0a6bf6f700..747501a66a 100644 --- a/renovate.json +++ b/renovate.json @@ -3,11 +3,8 @@ "extends": [ "config:best-practices", ":approveMajorUpdates", - ":maintainLockFilesWeekly", "group:postcss", - "group:linters", - "schedule:daily", - "schedule:automergeDaily" + "group:linters" ], "ignorePresets": [ ":semanticPrefixFixDepsChoreOthers", @@ -15,54 +12,25 @@ "helpers:pinGitHubActionDigests" ], "semanticCommits": "disabled", - "automergeStrategy": "merge-commit", "postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths", "npmDedupe"], + "platformAutomerge": false, "prConcurrentLimit": 5, - "internalChecksFilter": "strict", "packageRules": [ { "description": "Require approval for go and python minor version", - "matchDepNames": [ - "go", - "python", - "golang", - "docker.io/golang", - "docker.io/library/golang" - ], + "matchDepNames": ["go", "python", "golang", "docker.io/golang", "docker.io/library/golang"], "matchUpdateTypes": ["minor"], "dependencyDashboardApproval": true }, - { - "description": "Require dashboard approval for some deps", - "matchDepNames": [ - "bitnami/minio", - "github.com/go-ap/activitypub", - "github.com/nektos/act", - "gitea.com/gitea/act" - ], - "dependencyDashboardApproval": true - }, - { - "description": "Schedule some deps less frequently", - "matchDepNames": ["github.com/google/pprof"], - "extends": ["schedule:quarterly"] - }, { "description": "Group golang packages", - "matchDepNames": [ - "go", - "golang", - "docker.io/golang", - "docker.io/library/golang" - ], + "matchDepNames": ["go", "golang", "docker.io/golang", "docker.io/library/golang"], "groupName": "golang packages" }, { "description": "Group nodejs packages", "matchDepNames": ["node", "docker.io/node", "docker.io/library/node"], - "groupName": "nodejs packages", - "versionCompatibility": "^(?[^-]+)(?-.*)?$", - "versioning": "node" + "groupName": "nodejs packages" }, { "description": "Automerge renovate updates", @@ -72,20 +40,10 @@ "automerge": true }, { - "description": "Split minor and patch updates", - "matchDepNames": ["vue", "github.com/urfave/cli/v2", "swagger-ui-dist"], - "separateMinorPatch": true - }, - { - "description": "Automerge patch updates", - "matchDepNames": ["vue", "github.com/urfave/cli/v2", "swagger-ui-dist"], - "matchUpdateTypes": ["patch"], - "automerge": true - }, - { - "description": "Update renovate with higher prio to come through rate limit", + "description": "Update renovate only daily with higher prio to come through rate limit", "matchDatasources": ["docker"], "matchDepNames": ["ghcr.io/visualon/renovate"], + "extends": ["schedule:daily"], "prPriority": 10 }, { @@ -93,17 +51,6 @@ "matchDepNames": ["actions/cascading-pr"], "matchManagers": ["github-actions"], "enabled": false - }, - { - "description": "Automerge some packages when ci succeeds", - "extends": ["packages:linters"], - "matchDepNames": ["vitest", "vite-string-plugin"], - "automerge": true - }, - { - "description": "Hold back on some package updates for a few days", - "matchDepNames": ["monaco-editor"], - "minimumReleaseAge": "30 days" } ], "customManagers": [ @@ -123,14 +70,6 @@ "matchStrings": ["\\s+node-version: ['\"]?(?.+?)['\"]?\\s"], "depNameTemplate": "node", "datasourceTemplate": "node-version" - }, - { - "description": "Update deps inside Makefile", - "customType": "regex", - "fileMatch": ["^Makefile$"], - "matchStrings": [ - " \\?= (?.+?)@(?.+?) # renovate: datasource=(?.+?)\\s" - ] } ] } diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index 416d7e5181..4306df5cd8 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -144,6 +144,7 @@ func ArtifactContexter() func(next http.Handler) http.Handler { var task *actions.ActionTask if err == nil { + task, err = actions.GetTaskByID(req.Context(), tID) if err != nil { log.Error("Error runner api getting task by ID: %v", err) diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index 481cf70d33..cf0fe6c07c 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -144,12 +144,12 @@ func UploadPackageFile(ctx *context.Context) { return } - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index 07ea3eda34..c45e085a4d 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -310,12 +310,12 @@ func uploadFile(ctx *context.Context, fileFilter container.Set[string], fileKey return } - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go index c7e4544d52..30c80fc15e 100644 --- a/routers/api/packages/conda/conda.go +++ b/routers/api/packages/conda/conda.go @@ -174,12 +174,12 @@ func EnumeratePackages(ctx *context.Context) { } func UploadPackageFile(ctx *context.Context) { - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 2cb16daebc..e519766142 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -385,9 +385,9 @@ func EndUploadBlob(ctx *context.Context) { } return } - doClose := true + close := true defer func() { - if doClose { + if close { uploader.Close() } }() @@ -427,7 +427,7 @@ func EndUploadBlob(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - doClose = false + close = false if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go index f1d616724a..2cec75294f 100644 --- a/routers/api/packages/cran/cran.go +++ b/routers/api/packages/cran/cran.go @@ -151,12 +151,12 @@ func UploadBinaryPackageFile(ctx *context.Context) { } func uploadPackageFile(ctx *context.Context, compositeKey string, properties map[string]string) { - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index 8c05476cbc..241de3ac5d 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -127,12 +127,12 @@ func UploadPackageFile(ctx *context.Context) { return } - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index e66f3ee676..8232931134 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -90,12 +90,12 @@ func UploadPackage(ctx *context.Context) { return } - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index 56a07dbd43..d658066bb4 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -154,12 +154,12 @@ func resolvePackage(ctx *context.Context, ownerID int64, name, version string) ( } func UploadPackage(ctx *context.Context) { - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 26b0ae226e..09156ece6b 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -594,13 +594,13 @@ func UploadSymbolPackage(ctx *context.Context) { func processUploadedFile(ctx *context.Context, expectedType nuget_module.PackageType) (*nuget_module.Package, *packages_module.HashedBuffer, []io.Closer) { closables := make([]io.Closer, 0, 2) - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return nil, nil, closables } - if needToClose { + if close { closables = append(closables, upload) } diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index c59366992c..4de361c214 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -117,12 +117,12 @@ func GetRepositoryFile(ctx *context.Context) { } func UploadPackageFile(ctx *context.Context) { - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index ba5f4de080..d2fbcd01f0 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -197,12 +197,12 @@ func DownloadPackageFile(ctx *context.Context) { // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. func UploadPackageFile(ctx *context.Context) { - upload, needToClose, err := ctx.UploadStream() + upload, close, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if needToClose { + if close { defer upload.Close() } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 9ea210ee4e..98c9a2697f 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -192,17 +192,9 @@ func EditUser(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.EditUserOption) - // If either LoginSource or LoginName is given, the other must be present too. - if form.SourceID != nil || form.LoginName != nil { - if form.SourceID == nil || form.LoginName == nil { - ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", fmt.Errorf("source_id and login_name must be specified together")) - return - } - } - authOpts := &user_service.UpdateAuthOptions{ - LoginSource: optional.FromPtr(form.SourceID), - LoginName: optional.FromPtr(form.LoginName), + LoginSource: optional.FromNonDefault(form.SourceID), + LoginName: optional.Some(form.LoginName), Password: optional.FromNonDefault(form.Password), MustChangePassword: optional.FromPtr(form.MustChangePassword), ProhibitLogin: optional.FromPtr(form.ProhibitLogin), diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e4c848cd2f..b202e32e4e 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -93,7 +93,6 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" - "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -754,34 +753,6 @@ func Routes() *web.Route { m.Use(shared.Middlewares()...) - addActionsRoutes := func( - m *web.Route, - reqChecker func(ctx *context.APIContext), - act actions.API, - ) { - m.Group("/actions", func() { - m.Group("/secrets", func() { - m.Get("", reqToken(), reqChecker, act.ListActionsSecrets) - m.Combo("/{secretname}"). - Put(reqToken(), reqChecker, bind(api.CreateOrUpdateSecretOption{}), act.CreateOrUpdateSecret). - Delete(reqToken(), reqChecker, act.DeleteSecret) - }) - - m.Group("/variables", func() { - m.Get("", reqToken(), reqChecker, act.ListVariables) - m.Combo("/{variablename}"). - Get(reqToken(), reqChecker, act.GetVariable). - Delete(reqToken(), reqChecker, act.DeleteVariable). - Post(reqToken(), reqChecker, bind(api.CreateVariableOption{}), act.CreateVariable). - Put(reqToken(), reqChecker, bind(api.UpdateVariableOption{}), act.UpdateVariable) - }) - - m.Group("/runners", func() { - m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken) - }) - }) - } - m.Group("", func() { // Miscellaneous (no scope required) if setting.API.EnableSwagger { @@ -900,15 +871,6 @@ func Routes() *web.Route { Delete(user.DeleteSecret) }) - m.Group("/variables", func() { - m.Get("", user.ListVariables) - m.Combo("/{variablename}"). - Get(user.GetVariable). - Delete(user.DeleteVariable). - Post(bind(api.CreateVariableOption{}), user.CreateVariable). - Put(bind(api.UpdateVariableOption{}), user.UpdateVariable) - }) - m.Group("/runners", func() { m.Get("/registration-token", reqToken(), user.GetRegistrationToken) }) @@ -1012,8 +974,6 @@ func Routes() *web.Route { m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) m.Group("/{username}/{reponame}", func() { - m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff) - m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) @@ -1023,11 +983,17 @@ func Routes() *web.Route { m.Post("/accept", repo.AcceptTransfer) m.Post("/reject", repo.RejectTransfer) }, reqToken()) - addActionsRoutes( - m, - reqOwner(), - repo.NewAction(), - ) + m.Group("/actions", func() { + m.Group("/secrets", func() { + m.Combo("/{secretname}"). + Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret). + Delete(reqToken(), reqOwner(), repo.DeleteSecret) + }) + + m.Group("/runners", func() { + m.Get("/registration-token", reqToken(), reqOwner(), repo.GetRegistrationToken) + }) + }) m.Group("/hooks/git", func() { m.Combo("").Get(repo.ListGitHooks) m.Group("/{id}", func() { @@ -1419,11 +1385,18 @@ func Routes() *web.Route { m.Combo("/{username}").Get(reqToken(), org.IsMember). Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) }) - addActionsRoutes( - m, - reqOrgOwnership(), - org.NewAction(), - ) + m.Group("/actions", func() { + m.Group("/secrets", func() { + m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) + m.Combo("/{secretname}"). + Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret). + Delete(reqToken(), reqOrgOwnership(), org.DeleteSecret) + }) + + m.Group("/runners", func() { + m.Get("/registration-token", reqToken(), reqOrgOwnership(), org.GetRegistrationToken) + }) + }) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) m.Combo("/{username}").Get(org.IsPublicMember). diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go deleted file mode 100644 index 03a1fa8ccc..0000000000 --- a/routers/api/v1/org/action.go +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package org - -import ( - "errors" - "net/http" - - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" - secret_model "code.gitea.io/gitea/models/secret" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/api/v1/shared" - "code.gitea.io/gitea/routers/api/v1/utils" - actions_service "code.gitea.io/gitea/services/actions" - "code.gitea.io/gitea/services/context" - secret_service "code.gitea.io/gitea/services/secrets" -) - -// ListActionsSecrets list an organization's actions secrets -func (Action) ListActionsSecrets(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets - // --- - // summary: List an organization's actions secrets - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/SecretList" - // "404": - // "$ref": "#/responses/notFound" - - opts := &secret_model.FindSecretsOptions{ - OwnerID: ctx.Org.Organization.ID, - ListOptions: utils.GetListOptions(ctx), - } - - secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - apiSecrets := make([]*api.Secret, len(secrets)) - for k, v := range secrets { - apiSecrets[k] = &api.Secret{ - Name: v.Name, - Created: v.CreatedUnix.AsTime(), - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, apiSecrets) -} - -// create or update one secret of the organization -func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { - // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret - // --- - // summary: Create or Update a secret value in an organization - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of organization - // type: string - // required: true - // - name: secretname - // in: path - // description: name of the secret - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/CreateOrUpdateSecretOption" - // responses: - // "201": - // description: response when creating a secret - // "204": - // description: response when updating a secret - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - - _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data) - if err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err) - } else { - ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) - } - return - } - - if created { - ctx.Status(http.StatusCreated) - } else { - ctx.Status(http.StatusNoContent) - } -} - -// DeleteSecret delete one secret of the organization -func (Action) DeleteSecret(ctx *context.APIContext) { - // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret - // --- - // summary: Delete a secret in an organization - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of organization - // type: string - // required: true - // - name: secretname - // in: path - // description: name of the secret - // type: string - // required: true - // responses: - // "204": - // description: delete one secret of the organization - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname")) - if err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteSecret", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteSecret", err) - } else { - ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization -// GetRegistrationToken returns the token to register org runners -func (Action) GetRegistrationToken(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken - // --- - // summary: Get an organization's actions runner registration token - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/RegistrationToken" - - shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) -} - -// ListVariables list org-level variables -func (Action) ListVariables(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList - // --- - // summary: Get an org-level variables list - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/VariableList" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ - OwnerID: ctx.Org.Organization.ID, - ListOptions: utils.GetListOptions(ctx), - }) - if err != nil { - ctx.Error(http.StatusInternalServerError, "FindVariables", err) - return - } - - variables := make([]*api.ActionVariable, len(vars)) - for i, v := range vars { - variables[i] = &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, variables) -} - -// GetVariable get an org-level variable -func (Action) GetVariable(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable - // --- - // summary: Get an org-level variable - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/ActionVariable" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ctx.Org.Organization.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - variable := &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, - } - - ctx.JSON(http.StatusOK, variable) -} - -// DeleteVariable delete an org-level variable -func (Action) DeleteVariable(ctx *context.APIContext) { - // swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable - // --- - // summary: Delete an org-level variable - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/ActionVariable" - // "201": - // description: response when deleting a variable - // "204": - // description: response when deleting a variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - if err := actions_service.DeleteVariableByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("variablename")); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteVariableByName", err) - } else { - ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// CreateVariable create an org-level variable -func (Action) CreateVariable(ctx *context.APIContext) { - // swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable - // --- - // summary: Create an org-level variable - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/CreateVariableOption" - // responses: - // "201": - // description: response when creating an org-level variable - // "204": - // description: response when creating an org-level variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.CreateVariableOption) - - ownerID := ctx.Org.Organization.ID - variableName := ctx.Params("variablename") - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ownerID, - Name: variableName, - }) - if err != nil && !errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - return - } - if v != nil && v.ID > 0 { - ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName)) - return - } - - if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "CreateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// UpdateVariable update an org-level variable -func (Action) UpdateVariable(ctx *context.APIContext) { - // swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable - // --- - // summary: Update an org-level variable - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/UpdateVariableOption" - // responses: - // "201": - // description: response when updating an org-level variable - // "204": - // description: response when updating an org-level variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.UpdateVariableOption) - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ctx.Org.Organization.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - if opt.Name == "" { - opt.Name = ctx.Params("variablename") - } - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "UpdateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "UpdateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -var _ actions_service.API = new(Action) - -// Action implements actions_service.API -type Action struct{} - -// NewAction creates a new Action service -func NewAction() actions_service.API { - return Action{} -} diff --git a/routers/api/v1/org/runners.go b/routers/api/v1/org/runners.go new file mode 100644 index 0000000000..2a52bd8778 --- /dev/null +++ b/routers/api/v1/org/runners.go @@ -0,0 +1,31 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org + +import ( + "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" +) + +// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization + +// GetRegistrationToken returns the token to register org runners +func GetRegistrationToken(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken + // --- + // summary: Get an organization's actions runner registration token + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/RegistrationToken" + + shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) +} diff --git a/routers/api/v1/org/secrets.go b/routers/api/v1/org/secrets.go new file mode 100644 index 0000000000..abb6bb26c4 --- /dev/null +++ b/routers/api/v1/org/secrets.go @@ -0,0 +1,166 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org + +import ( + "errors" + "net/http" + + "code.gitea.io/gitea/models/db" + secret_model "code.gitea.io/gitea/models/secret" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/context" + secret_service "code.gitea.io/gitea/services/secrets" +) + +// ListActionsSecrets list an organization's actions secrets +func ListActionsSecrets(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets + // --- + // summary: List an organization's actions secrets + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/SecretList" + // "404": + // "$ref": "#/responses/notFound" + + opts := &secret_model.FindSecretsOptions{ + OwnerID: ctx.Org.Organization.ID, + ListOptions: utils.GetListOptions(ctx), + } + + secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + apiSecrets := make([]*api.Secret, len(secrets)) + for k, v := range secrets { + apiSecrets[k] = &api.Secret{ + Name: v.Name, + Created: v.CreatedUnix.AsTime(), + } + } + + ctx.SetTotalCountHeader(count) + ctx.JSON(http.StatusOK, apiSecrets) +} + +// create or update one secret of the organization +func CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret + // --- + // summary: Create or Update a secret value in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateOrUpdateSecretOption" + // responses: + // "201": + // description: response when creating a secret + // "204": + // description: response when updating a secret + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + + _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data) + if err != nil { + if errors.Is(err, util.ErrInvalidArgument) { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + } else if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err) + } else { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + } + return + } + + if created { + ctx.Status(http.StatusCreated) + } else { + ctx.Status(http.StatusNoContent) + } +} + +// DeleteSecret delete one secret of the organization +func DeleteSecret(ctx *context.APIContext) { + // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret + // --- + // summary: Delete a secret in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // responses: + // "204": + // description: delete one secret of the organization + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + + err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname")) + if err != nil { + if errors.Is(err, util.ErrInvalidArgument) { + ctx.Error(http.StatusBadRequest, "DeleteSecret", err) + } else if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "DeleteSecret", err) + } else { + ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) + } + return + } + + ctx.Status(http.StatusNoContent) +} diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 311cfca6e9..e0af276c71 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -7,78 +7,15 @@ import ( "errors" "net/http" - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" - secret_model "code.gitea.io/gitea/models/secret" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/api/v1/shared" - "code.gitea.io/gitea/routers/api/v1/utils" - actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) -// ListActionsSecrets list an repo's actions secrets -func (Action) ListActionsSecrets(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/actions/secrets repository repoListActionsSecrets - // --- - // summary: List an repo's actions secrets - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: owner of the repository - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/SecretList" - // "404": - // "$ref": "#/responses/notFound" - - repo := ctx.Repo.Repository - - opts := &secret_model.FindSecretsOptions{ - RepoID: repo.ID, - ListOptions: utils.GetListOptions(ctx), - } - - secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - apiSecrets := make([]*api.Secret, len(secrets)) - for k, v := range secrets { - apiSecrets[k] = &api.Secret{ - Name: v.Name, - Created: v.CreatedUnix.AsTime(), - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, apiSecrets) -} - // create or update one secret of the repository -func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { +func CreateOrUpdateSecret(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret // --- // summary: Create or Update a secret value in a repository @@ -141,7 +78,7 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { } // DeleteSecret delete one secret of the repository -func (Action) DeleteSecret(ctx *context.APIContext) { +func DeleteSecret(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secretname} repository deleteRepoSecret // --- // summary: Delete a secret in a repository @@ -190,330 +127,3 @@ func (Action) DeleteSecret(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } - -// GetVariable get a repo-level variable -func (Action) GetVariable(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/actions/variables/{variablename} repository getRepoVariable - // --- - // summary: Get a repo-level variable - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: name of the owner - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/ActionVariable" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - RepoID: ctx.Repo.Repository.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - variable := &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, - } - - ctx.JSON(http.StatusOK, variable) -} - -// DeleteVariable delete a repo-level variable -func (Action) DeleteVariable(ctx *context.APIContext) { - // swagger:operation DELETE /repos/{owner}/{repo}/actions/variables/{variablename} repository deleteRepoVariable - // --- - // summary: Delete a repo-level variable - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: name of the owner - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/ActionVariable" - // "201": - // description: response when deleting a variable - // "204": - // description: response when deleting a variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - if err := actions_service.DeleteVariableByName(ctx, 0, ctx.Repo.Repository.ID, ctx.Params("variablename")); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteVariableByName", err) - } else { - ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// CreateVariable create a repo-level variable -func (Action) CreateVariable(ctx *context.APIContext) { - // swagger:operation POST /repos/{owner}/{repo}/actions/variables/{variablename} repository createRepoVariable - // --- - // summary: Create a repo-level variable - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: name of the owner - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/CreateVariableOption" - // responses: - // "201": - // description: response when creating a repo-level variable - // "204": - // description: response when creating a repo-level variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.CreateVariableOption) - - repoID := ctx.Repo.Repository.ID - variableName := ctx.Params("variablename") - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - RepoID: repoID, - Name: variableName, - }) - if err != nil && !errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - return - } - if v != nil && v.ID > 0 { - ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName)) - return - } - - if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "CreateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// UpdateVariable update a repo-level variable -func (Action) UpdateVariable(ctx *context.APIContext) { - // swagger:operation PUT /repos/{owner}/{repo}/actions/variables/{variablename} repository updateRepoVariable - // --- - // summary: Update a repo-level variable - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: name of the owner - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/UpdateVariableOption" - // responses: - // "201": - // description: response when updating a repo-level variable - // "204": - // description: response when updating a repo-level variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.UpdateVariableOption) - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - RepoID: ctx.Repo.Repository.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - if opt.Name == "" { - opt.Name = ctx.Params("variablename") - } - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "UpdateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "UpdateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// ListVariables list repo-level variables -func (Action) ListVariables(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/actions/variables repository getRepoVariablesList - // --- - // summary: Get repo-level variables list - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: name of the owner - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repository - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/VariableList" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ - RepoID: ctx.Repo.Repository.ID, - ListOptions: utils.GetListOptions(ctx), - }) - if err != nil { - ctx.Error(http.StatusInternalServerError, "FindVariables", err) - return - } - - variables := make([]*api.ActionVariable, len(vars)) - for i, v := range vars { - variables[i] = &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, variables) -} - -// GetRegistrationToken returns the token to register repo runners -func (Action) GetRegistrationToken(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken - // --- - // summary: Get a repository's actions runner registration token - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: owner of the repo - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repo - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/RegistrationToken" - - shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) -} - -var _ actions_service.API = new(Action) - -// Action implements actions_service.API -type Action struct{} - -// NewAction creates a new Action service -func NewAction() actions_service.API { - return Action{} -} diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go deleted file mode 100644 index cfd61d768c..0000000000 --- a/routers/api/v1/repo/compare.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "net/http" - "strings" - - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/gitrepo" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/services/context" - "code.gitea.io/gitea/services/convert" -) - -// CompareDiff compare two branches or commits -func CompareDiff(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} repository repoCompareDiff - // --- - // summary: Get commit comparison information - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: owner of the repo - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repo - // type: string - // required: true - // - name: basehead - // in: path - // description: compare two branches or commits - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/Compare" - // "404": - // "$ref": "#/responses/notFound" - - if ctx.Repo.GitRepo == nil { - gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository) - if err != nil { - ctx.Error(http.StatusInternalServerError, "OpenRepository", err) - return - } - ctx.Repo.GitRepo = gitRepo - defer gitRepo.Close() - } - - infoPath := ctx.Params("*") - infos := []string{ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository.DefaultBranch} - if infoPath != "" { - infos = strings.SplitN(infoPath, "...", 2) - if len(infos) != 2 { - if infos = strings.SplitN(infoPath, "..", 2); len(infos) != 2 { - infos = []string{ctx.Repo.Repository.DefaultBranch, infoPath} - } - } - } - - _, _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{ - Base: infos[0], - Head: infos[1], - }) - if ctx.Written() { - return - } - defer headGitRepo.Close() - - verification := ctx.FormString("verification") == "" || ctx.FormBool("verification") - files := ctx.FormString("files") == "" || ctx.FormBool("files") - - apiCommits := make([]*api.Commit, 0, len(ci.Commits)) - userCache := make(map[string]*user_model.User) - for i := 0; i < len(ci.Commits); i++ { - apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache, - convert.ToCommitOptions{ - Stat: true, - Verification: verification, - Files: files, - }) - if err != nil { - ctx.ServerError("toCommit", err) - return - } - apiCommits = append(apiCommits, apiCommit) - } - - ctx.JSON(http.StatusOK, &api.Compare{ - TotalCommits: len(ci.Commits), - Commits: apiCommits, - }) -} diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 34ccc929a5..712cba7455 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -302,7 +302,7 @@ func GetArchive(ctx *context.APIContext) { func archiveDownload(ctx *context.APIContext) { uri := ctx.Params("*") - aReq, err := archiver_service.NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) if err != nil { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { ctx.Error(http.StatusBadRequest, "unknown archive format", err) @@ -326,12 +326,6 @@ func archiveDownload(ctx *context.APIContext) { func download(ctx *context.APIContext, archiveName string, archiver *repo_model.RepoArchiver) { downloadName := ctx.Repo.Repository.Name + "-" + archiveName - // Add nix format link header so tarballs lock correctly: - // https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md - ctx.Resp.Header().Add("Link", fmt.Sprintf("<%s/archive/%s.tar.gz?rev=%s>; rel=\"immutable\"", - ctx.Repo.Repository.APIURL(), - archiver.CommitID, archiver.CommitID)) - rPath := archiver.RelativePath() if setting.RepoArchive.Storage.MinioConfig.ServeDirect { // If we have a signed url (S3, object storage), redirect to this directly. diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 35c1fdcc0c..0d304dd66d 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -217,6 +217,7 @@ func SearchIssues(ctx *context.APIContext) { var includedAnyLabels []int64 { + labels := ctx.FormTrim("labels") var includedLabelNames []string if len(labels) > 0 { diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 819859b991..c3005dee3b 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -323,6 +323,10 @@ func ListRepoIssueComments(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadIssues", err) return } + if err := comments.LoadPosters(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadPosters", err) + return + } if err := comments.LoadAttachments(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) return diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 2a896de4fe..864644e1ef 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -180,6 +180,7 @@ func ListPushMirrors(ctx *context.APIContext) { if err == nil { responsePushMirrors = append(responsePushMirrors, m) } + } ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(count) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 852ec78ade..eec3c49bc4 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1058,6 +1058,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) isSameRepo = true headUser = ctx.Repo.Owner headBranch = headInfos[0] + } else if len(headInfos) == 2 { headUser, err = user_model.GetUserByName(ctx, headInfos[0]) if err != nil { @@ -1071,16 +1072,18 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headBranch = headInfos[1] // The head repository can also point to the same repo isSameRepo = ctx.Repo.Owner.ID == headUser.ID + } else { ctx.NotFound() return nil, nil, nil, nil, "", "" } ctx.Repo.PullRequest.SameRepo = isSameRepo - log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch) + log.Info("Base branch: %s", baseBranch) + log.Info("Repo path: %s", ctx.Repo.GitRepo.Path) // Check if base branch is valid. - if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) { - ctx.NotFound("BaseNotExist") + if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) { + ctx.NotFound("IsBranchExist") return nil, nil, nil, nil, "", "" } @@ -1143,7 +1146,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if head branch is valid. - if !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) { + if !headGitRepo.IsBranchExist(headBranch) { headGitRepo.Close() ctx.NotFound() return nil, nil, nil, nil, "", "" diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 6799e43c73..77c0d25e2a 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -4,7 +4,6 @@ package repo import ( - "errors" "fmt" "net/http" "strings" @@ -520,11 +519,7 @@ func CreatePullReview(ctx *context.APIContext) { // create review and associate all pending review comments review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil) if err != nil { - if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { - ctx.Error(http.StatusUnprocessableEntity, "", err) - } else { - ctx.Error(http.StatusInternalServerError, "SubmitReview", err) - } + ctx.Error(http.StatusInternalServerError, "SubmitReview", err) return } @@ -612,11 +607,7 @@ func SubmitPullReview(ctx *context.APIContext) { // create review and associate all pending review comments review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil) if err != nil { - if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { - ctx.Error(http.StatusUnprocessableEntity, "", err) - } else { - ctx.Error(http.StatusInternalServerError, "SubmitReview", err) - } + ctx.Error(http.StatusInternalServerError, "SubmitReview", err) return } @@ -884,6 +875,7 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions } if ctx.Repo.Repository.Owner.IsOrganization() && len(opts.TeamReviewers) > 0 { + teamReviewers := make([]*organization.Team, 0, len(opts.TeamReviewers)) for _, t := range opts.TeamReviewers { var teamReviewer *organization.Team diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 057282b210..c6495c3de8 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -231,18 +231,17 @@ func CreateRelease(ctx *context.APIContext) { form.Target = ctx.Repo.Repository.DefaultBranch } rel = &repo_model.Release{ - RepoID: ctx.Repo.Repository.ID, - PublisherID: ctx.Doer.ID, - Publisher: ctx.Doer, - TagName: form.TagName, - Target: form.Target, - Title: form.Title, - Note: form.Note, - IsDraft: form.IsDraft, - IsPrerelease: form.IsPrerelease, - HideArchiveLinks: form.HideArchiveLinks, - IsTag: false, - Repo: ctx.Repo.Repository, + RepoID: ctx.Repo.Repository.ID, + PublisherID: ctx.Doer.ID, + Publisher: ctx.Doer, + TagName: form.TagName, + Target: form.Target, + Title: form.Title, + Note: form.Note, + IsDraft: form.IsDraft, + IsPrerelease: form.IsPrerelease, + IsTag: false, + Repo: ctx.Repo.Repository, } if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil { if repo_model.IsErrReleaseAlreadyExist(err) { @@ -262,7 +261,6 @@ func CreateRelease(ctx *context.APIContext) { rel.Note = form.Note rel.IsDraft = form.IsDraft rel.IsPrerelease = form.IsPrerelease - rel.HideArchiveLinks = form.HideArchiveLinks rel.PublisherID = ctx.Doer.ID rel.IsTag = false rel.Repo = ctx.Repo.Repository @@ -343,9 +341,6 @@ func EditRelease(ctx *context.APIContext) { if form.IsPrerelease != nil { rel.IsPrerelease = *form.IsPrerelease } - if form.HideArchiveLinks != nil { - rel.HideArchiveLinks = *form.HideArchiveLinks - } if err := release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil, false); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) return diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 66eb227c19..562c3eb64c 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -845,15 +845,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { newHasWiki = *opts.HasWiki } if currHasWiki || newHasWiki { - wikiPermissions := repo.MustGetUnit(ctx, unit_model.TypeWiki).DefaultPermissions - if opts.GloballyEditableWiki != nil { - if *opts.GloballyEditableWiki { - wikiPermissions = repo_model.UnitAccessModeWrite - } else { - wikiPermissions = repo_model.UnitAccessModeRead - } - } - if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { @@ -873,10 +864,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } else if newHasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() { config := &repo_model.UnitConfig{} units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeWiki, - Config: config, - DefaultPermissions: wikiPermissions, + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: config, }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) } else if !newHasWiki { @@ -886,14 +876,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if !unit_model.TypeWiki.UnitGlobalDisabled() { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) } - } else if *opts.GloballyEditableWiki { - config := &repo_model.UnitConfig{} - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeWiki, - Config: config, - DefaultPermissions: wikiPermissions, - }) } } @@ -1094,6 +1076,7 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error { // update MirrorInterval if opts.MirrorInterval != nil { + // MirrorInterval should be a duration interval, err := time.ParseDuration(*opts.MirrorInterval) if err != nil { diff --git a/routers/api/v1/repo/runners.go b/routers/api/v1/repo/runners.go new file mode 100644 index 0000000000..fe133b311d --- /dev/null +++ b/routers/api/v1/repo/runners.go @@ -0,0 +1,34 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "code.gitea.io/gitea/routers/api/v1/shared" + "code.gitea.io/gitea/services/context" +) + +// GetRegistrationToken returns the token to register repo runners +func GetRegistrationToken(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken + // --- + // summary: Get a repository's actions runner registration token + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/RegistrationToken" + + shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) +} diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index b498f0e792..84ec3dd91c 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -60,12 +60,6 @@ func ListTags(ctx *context.APIContext) { apiTags := make([]*api.Tag, len(tags)) for i := range tags { - tags[i].ArchiveDownloadCount, err = repo_model.GetArchiveDownloadCountForTagName(ctx, ctx.Repo.Repository.ID, tags[i].Name) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTagArchiveDownloadCountForName", err) - return - } - apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) } @@ -117,13 +111,6 @@ func GetAnnotatedTag(ctx *context.APIContext) { if err != nil { ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err) } - - tag.ArchiveDownloadCount, err = repo_model.GetArchiveDownloadCountForTagName(ctx, ctx.Repo.Repository.ID, tag.Name) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTagArchiveDownloadCountForName", err) - return - } - ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit)) } } @@ -163,13 +150,6 @@ func GetTag(ctx *context.APIContext) { ctx.NotFound(tagName) return } - - tag.ArchiveDownloadCount, err = repo_model.GetArchiveDownloadCountForTagName(ctx, ctx.Repo.Repository.ID, tag.Name) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTagArchiveDownloadCountForName", err) - return - } - ctx.JSON(http.StatusOK, convert.ToTag(ctx.Repo.Repository, tag)) } @@ -238,13 +218,6 @@ func CreateTag(ctx *context.APIContext) { ctx.InternalServerError(err) return } - - tag.ArchiveDownloadCount, err = repo_model.GetArchiveDownloadCountForTagName(ctx, ctx.Repo.Repository.ID, tag.Name) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTagArchiveDownloadCountForName", err) - return - } - ctx.JSON(http.StatusCreated, convert.ToTag(ctx.Repo.Repository, tag)) } diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index 1b92c7bceb..f18ea087c4 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -193,7 +193,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi } // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) // Get last change information. lastCommit, err := wikiRepo.GetCommitByPath(pageFilename) @@ -432,7 +432,7 @@ func ListPageRevisions(ctx *context.APIContext) { } // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) page := ctx.FormInt("page") if page <= 1 { @@ -442,7 +442,7 @@ func ListPageRevisions(ctx *context.APIContext) { // get Commit Count commitsHistory, err := wikiRepo.CommitsByFileAndRange( git.CommitsByFileAndRangeOptions{ - Revision: ctx.Repo.Repository.GetWikiBranchName(), + Revision: "master", File: pageFilename, Page: page, }) @@ -478,6 +478,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) { wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository) if err != nil { + if git.IsErrNotExist(err) || err.Error() == "no such file or directory" { ctx.NotFound(err) } else { @@ -486,7 +487,7 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) return nil, nil } - commit, err := wikiRepo.GetBranchCommit(ctx.Repo.Repository.GetWikiBranchName()) + commit, err := wikiRepo.GetBranchCommit("master") if err != nil { if git.IsErrNotExist(err) { ctx.NotFound(err) diff --git a/routers/api/v1/swagger/action.go b/routers/api/v1/swagger/action.go index 665f4d0b85..3771780718 100644 --- a/routers/api/v1/swagger/action.go +++ b/routers/api/v1/swagger/action.go @@ -18,17 +18,3 @@ type swaggerResponseSecret struct { // in:body Body api.Secret `json:"body"` } - -// ActionVariable -// swagger:response ActionVariable -type swaggerResponseActionVariable struct { - // in:body - Body api.ActionVariable `json:"body"` -} - -// VariableList -// swagger:response VariableList -type swaggerResponseVariableList struct { - // in:body - Body []api.ActionVariable `json:"body"` -} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 6a94c21002..2886b865e8 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -199,10 +199,4 @@ type swaggerParameterBodies struct { // in:body CreateOrUpdateSecretOption api.CreateOrUpdateSecretOption - - // in:body - CreateVariableOption api.CreateVariableOption - - // in:body - UpdateVariableOption api.UpdateVariableOption } diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index b55ea1d0a9..c7fa98a697 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -421,9 +421,3 @@ type swaggerBlockedUserList struct { // in:body Body []api.BlockedUser `json:"body"` } - -// swagger:response Compare -type swaggerCompare struct { - // in:body - Body api.Compare `json:"body"` -} diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go index bf78c2c864..babb8c0cf7 100644 --- a/routers/api/v1/user/action.go +++ b/routers/api/v1/user/action.go @@ -7,13 +7,9 @@ import ( "errors" "net/http" - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/api/v1/utils" - actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) @@ -105,249 +101,3 @@ func DeleteSecret(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } - -// CreateVariable create a user-level variable -func CreateVariable(ctx *context.APIContext) { - // swagger:operation POST /user/actions/variables/{variablename} user createUserVariable - // --- - // summary: Create a user-level variable - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/CreateVariableOption" - // responses: - // "201": - // description: response when creating a variable - // "204": - // description: response when creating a variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.CreateVariableOption) - - ownerID := ctx.Doer.ID - variableName := ctx.Params("variablename") - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ownerID, - Name: variableName, - }) - if err != nil && !errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - return - } - if v != nil && v.ID > 0 { - ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName)) - return - } - - if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "CreateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// UpdateVariable update a user-level variable which is created by current doer -func UpdateVariable(ctx *context.APIContext) { - // swagger:operation PUT /user/actions/variables/{variablename} user updateUserVariable - // --- - // summary: Update a user-level variable which is created by current doer - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/UpdateVariableOption" - // responses: - // "201": - // description: response when updating a variable - // "204": - // description: response when updating a variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.UpdateVariableOption) - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ctx.Doer.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - if opt.Name == "" { - opt.Name = ctx.Params("variablename") - } - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "UpdateVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "UpdateVariable", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// DeleteVariable delete a user-level variable which is created by current doer -func DeleteVariable(ctx *context.APIContext) { - // swagger:operation DELETE /user/actions/variables/{variablename} user deleteUserVariable - // --- - // summary: Delete a user-level variable which is created by current doer - // produces: - // - application/json - // parameters: - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "201": - // description: response when deleting a variable - // "204": - // description: response when deleting a variable - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - if err := actions_service.DeleteVariableByName(ctx, ctx.Doer.ID, 0, ctx.Params("variablename")); err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteVariableByName", err) - } else { - ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} - -// GetVariable get a user-level variable which is created by current doer -func GetVariable(ctx *context.APIContext) { - // swagger:operation GET /user/actions/variables/{variablename} user getUserVariable - // --- - // summary: Get a user-level variable which is created by current doer - // produces: - // - application/json - // parameters: - // - name: variablename - // in: path - // description: name of the variable - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/ActionVariable" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ctx.Doer.ID, - Name: ctx.Params("variablename"), - }) - if err != nil { - if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) - } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) - } - return - } - - variable := &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, - } - - ctx.JSON(http.StatusOK, variable) -} - -// ListVariables list user-level variables -func ListVariables(ctx *context.APIContext) { - // swagger:operation GET /user/actions/variables user getUserVariablesList - // --- - // summary: Get the user-level list of variables which is created by current doer - // produces: - // - application/json - // parameters: - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/VariableList" - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ - OwnerID: ctx.Doer.ID, - ListOptions: utils.GetListOptions(ctx), - }) - if err != nil { - ctx.Error(http.StatusInternalServerError, "FindVariables", err) - return - } - - variables := make([]*api.ActionVariable, len(vars)) - for i, v := range vars { - variables[i] = &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, variables) -} diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 5a2f995e1b..dcf5da0b2e 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -10,7 +10,6 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" @@ -134,7 +133,7 @@ func GetGPGKey(ctx *context.APIContext) { // CreateUserGPGKey creates new GPG key to given user by ID. func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) { - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageGPGKeys) { ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) return } @@ -275,7 +274,7 @@ func DeleteGPGKey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageGPGKeys) { ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) return } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index d9456e7ec6..bcbfd93bd3 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -199,7 +199,7 @@ func GetPublicKey(ctx *context.APIContext) { // CreateUserPublicKey creates new public key to given user by ID. func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) { - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) return } @@ -269,7 +269,7 @@ func DeletePublicKey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) return } diff --git a/routers/common/compare.go b/routers/common/compare.go deleted file mode 100644 index 4d1cc2f0d8..0000000000 --- a/routers/common/compare.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package common - -import ( - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" -) - -// CompareInfo represents the collected results from ParseCompareInfo -type CompareInfo struct { - HeadUser *user_model.User - HeadRepo *repo_model.Repository - HeadGitRepo *git.Repository - CompareInfo *git.CompareInfo - BaseBranch string - HeadBranch string - DirectComparison bool -} diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index da9fa20082..cb356a184a 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -259,6 +259,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName), }) return + } } diff --git a/routers/utils/utils.go b/routers/utils/utils.go index 3035073d5c..1f4d11fd3c 100644 --- a/routers/utils/utils.go +++ b/routers/utils/utils.go @@ -5,10 +5,26 @@ package utils import ( "html" + "net/url" "strings" + + "code.gitea.io/gitea/modules/setting" ) // SanitizeFlashErrorString will sanitize a flash error string func SanitizeFlashErrorString(x string) string { return strings.ReplaceAll(html.EscapeString(x), "\n", "
    ") } + +// IsExternalURL checks if rawURL points to an external URL like http://example.com +func IsExternalURL(rawURL string) bool { + parsed, err := url.Parse(rawURL) + if err != nil { + return true + } + appURL, _ := url.Parse(setting.AppURL) + if len(parsed.Host) != 0 && strings.Replace(parsed.Host, "www.", "", 1) != strings.Replace(appURL.Host, "www.", "", 1) { + return true + } + return false +} diff --git a/routers/utils/utils_test.go b/routers/utils/utils_test.go index 6e7f3c33cd..440aad87c6 100644 --- a/routers/utils/utils_test.go +++ b/routers/utils/utils_test.go @@ -5,8 +5,47 @@ package utils import ( "testing" + + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" ) +func TestIsExternalURL(t *testing.T) { + setting.AppURL = "https://try.gitea.io/" + type test struct { + Expected bool + RawURL string + } + newTest := func(expected bool, rawURL string) test { + return test{Expected: expected, RawURL: rawURL} + } + for _, test := range []test{ + newTest(false, + "https://try.gitea.io"), + newTest(true, + "https://example.com/"), + newTest(true, + "//example.com"), + newTest(true, + "http://example.com"), + newTest(false, + "a/"), + newTest(false, + "https://try.gitea.io/test?param=false"), + newTest(false, + "test?param=false"), + newTest(false, + "//try.gitea.io/test?param=false"), + newTest(false, + "/hey/hey/hey#3244"), + newTest(true, + "://missing protocol scheme"), + } { + assert.Equal(t, test.Expected, IsExternalURL(test.RawURL)) + } +} + func TestSanitizeFlashErrorString(t *testing.T) { tests := []struct { name string diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 799b7e8a84..ba487d1045 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -129,7 +129,6 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { UserDN: form.UserDN, BindPassword: form.BindPassword, UserBase: form.UserBase, - DefaultDomainName: form.DefaultDomainName, AttributeUsername: form.AttributeUsername, AttributeName: form.AttributeName, AttributeSurname: form.AttributeSurname, diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 50fd760d80..1c55256db4 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/eventsource" - "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/session" @@ -27,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/routers/utils" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/context" @@ -372,16 +372,17 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe return setting.AppSubURL + "/" } - redirectTo := ctx.GetSiteCookie("redirect_to") - if redirectTo != "" { + if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { middleware.DeleteRedirectToCookie(ctx.Resp) - } - if obeyRedirect { - return ctx.RedirectToFirst(redirectTo) - } - if !httplib.IsRiskyRedirectURL(redirectTo) { + if obeyRedirect { + ctx.RedirectToFirst(redirectTo) + } return redirectTo } + + if obeyRedirect { + ctx.Redirect(setting.AppSubURL + "/") + } return setting.AppSubURL + "/" } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 3ad7a4738e..f5ca0bda5e 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -5,7 +5,6 @@ package auth import ( go_context "context" - "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -36,7 +35,6 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" - remote_service "code.gitea.io/gitea/services/remote" user_service "code.gitea.io/gitea/services/user" "gitea.com/go-chi/binding" @@ -861,19 +859,13 @@ func SignInOAuth(ctx *context.Context) { return } - codeChallenge, err := generateCodeChallenge(ctx) - if err != nil { - ctx.ServerError("SignIn", fmt.Errorf("could not generate code_challenge: %w", err)) - return - } - - if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp, codeChallenge); err != nil { + if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { if strings.Contains(err.Error(), "no provider for ") { if err = oauth2.ResetOAuth2(ctx); err != nil { ctx.ServerError("SignIn", err) return } - if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp, codeChallenge); err != nil { + if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { ctx.ServerError("SignIn", err) } return @@ -1210,42 +1202,9 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model ctx.Redirect(setting.AppSubURL + "/user/two_factor") } -// generateCodeChallenge stores a code verifier in the session and returns a S256 code challenge for PKCE -func generateCodeChallenge(ctx *context.Context) (codeChallenge string, err error) { - codeVerifier, err := util.CryptoRandomString(43) // 256/log2(62) = 256 bits of entropy (each char having log2(62) of randomness) - if err != nil { - return "", err - } - if err = ctx.Session.Set("CodeVerifier", codeVerifier); err != nil { - return "", err - } - return encodeCodeChallenge(codeVerifier) -} - -func encodeCodeChallenge(codeVerifier string) (string, error) { - hasher := sha256.New() - _, err := io.WriteString(hasher, codeVerifier) - codeChallenge := base64.RawURLEncoding.EncodeToString(hasher.Sum(nil)) - return codeChallenge, err -} - // OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful // login the user func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { - gothUser, err := oAuth2FetchUser(ctx, authSource, request, response) - if err != nil { - return nil, goth.User{}, err - } - - if _, _, err := remote_service.MaybePromoteRemoteUser(ctx, authSource, gothUser.UserID, gothUser.Email); err != nil { - return nil, goth.User{}, err - } - - u, err := oAuth2GothUserToUser(request.Context(), authSource, gothUser) - return u, gothUser, err -} - -func oAuth2FetchUser(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (goth.User, error) { oauth2Source := authSource.Cfg.(*oauth2.Source) // Make sure that the response is not an error response. @@ -1257,46 +1216,40 @@ func oAuth2FetchUser(ctx *context.Context, authSource *auth.Source, request *htt // Delete the goth session err := gothic.Logout(response, request) if err != nil { - return goth.User{}, err + return nil, goth.User{}, err } - return goth.User{}, errCallback{ + return nil, goth.User{}, errCallback{ Code: errorName, Description: errorDescription, } } // Proceed to authenticate through goth. - codeVerifier, _ := ctx.Session.Get("CodeVerifier").(string) - _ = ctx.Session.Delete("CodeVerifier") - gothUser, err := oauth2Source.Callback(request, response, codeVerifier) + gothUser, err := oauth2Source.Callback(request, response) if err != nil { if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") { log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) err = fmt.Errorf("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) } - return goth.User{}, err + return nil, goth.User{}, err } if oauth2Source.RequiredClaimName != "" { claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName] if !has { - return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} + return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } if oauth2Source.RequiredClaimValue != "" { groups := claimValueToStringSet(claimInterface) if !groups.Contains(oauth2Source.RequiredClaimValue) { - return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} + return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } } } - return gothUser, nil -} - -func oAuth2GothUserToUser(ctx go_context.Context, authSource *auth.Source, gothUser goth.User) (*user_model.User, error) { user := &user_model.User{ LoginName: gothUser.UserID, LoginType: auth.OAuth2, @@ -1305,28 +1258,27 @@ func oAuth2GothUserToUser(ctx go_context.Context, authSource *auth.Source, gothU hasUser, err := user_model.GetUser(ctx, user) if err != nil { - return nil, err + return nil, goth.User{}, err } if hasUser { - return user, nil + return user, gothUser, nil } - log.Debug("no user found for LoginName %v, LoginSource %v, LoginType %v", user.LoginName, user.LoginSource, user.LoginType) // search in external linked users externalLoginUser := &user_model.ExternalLoginUser{ ExternalID: gothUser.UserID, LoginSourceID: authSource.ID, } - hasUser, err = user_model.GetExternalLogin(ctx, externalLoginUser) + hasUser, err = user_model.GetExternalLogin(request.Context(), externalLoginUser) if err != nil { - return nil, err + return nil, goth.User{}, err } if hasUser { - user, err = user_model.GetUserByID(ctx, externalLoginUser.UserID) - return user, err + user, err = user_model.GetUserByID(request.Context(), externalLoginUser.UserID) + return user, gothUser, err } // no user found to login - return nil, nil + return nil, gothUser, nil } diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 3726daee93..4339d9d1eb 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -93,10 +93,3 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { assert.Equal(t, user.Email, oidcToken.Email) assert.Equal(t, user.IsActive, oidcToken.EmailVerified) } - -func TestEncodeCodeChallenge(t *testing.T) { - // test vector from https://datatracker.ietf.org/doc/html/rfc7636#page-18 - codeChallenge, err := encodeCodeChallenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") - assert.NoError(t, err) - assert.Equal(t, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", codeChallenge) -} diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index d15a8b814c..53494df630 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" @@ -309,9 +310,11 @@ func MustChangePasswordPost(ctx *context.Context) { log.Trace("User updated password: %s", ctx.Doer.Name) - redirectTo := ctx.GetSiteCookie("redirect_to") - if redirectTo != "" { + if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { middleware.DeleteRedirectToCookie(ctx.Resp) + ctx.RedirectToFirst(redirectTo) + return } - ctx.RedirectToFirst(redirectTo) + + ctx.Redirect(setting.AppSubURL + "/") } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 821228b347..38f9b85ace 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -104,7 +104,7 @@ func Projects(ctx *context.Context) { } for _, project := range projects { - project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description) + project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render? } err = shared_user.LoadHeaderCount(ctx) @@ -372,7 +372,7 @@ func ViewProject(ctx *context.Context) { } } - project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description) + project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render? ctx.Data["LinkedPRs"] = linkedPrsMap ctx.Data["PageIsViewProjects"] = true ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index dc21f1a4ed..6176ac1278 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -689,6 +689,7 @@ func ArtifactsDownloadView(ctx *context_module.Context) { writer := zip.NewWriter(ctx.Resp) defer writer.Close() for _, art := range artifacts { + f, err := storage.ActionsArtifacts.Open(art.StoragePath) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 33491ec696..718454e063 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -212,6 +212,8 @@ func SearchCommits(ctx *context.Context) { // FileHistory show a file's reversions func FileHistory(ctx *context.Context) { + ctx.Data["IsRepoToolbarCommits"] = true + fileName := ctx.Repo.TreePath if len(fileName) == 0 { Commits(ctx) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index a61e23add3..93c224b7be 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -35,7 +35,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/gitdiff" @@ -186,10 +185,21 @@ func setCsvCompareContext(ctx *context.Context) { } } +// CompareInfo represents the collected results from ParseCompareInfo +type CompareInfo struct { + HeadUser *user_model.User + HeadRepo *repo_model.Repository + HeadGitRepo *git.Repository + CompareInfo *git.CompareInfo + BaseBranch string + HeadBranch string + DirectComparison bool +} + // ParseCompareInfo parse compare info between two commit for preparing comparing references -func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { +func ParseCompareInfo(ctx *context.Context) *CompareInfo { baseRepo := ctx.Repo.Repository - ci := &common.CompareInfo{} + ci := &CompareInfo{} fileOnly := ctx.FormBool("file-only") @@ -566,7 +576,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { // PrepareCompareDiff renders compare diff page func PrepareCompareDiff( ctx *context.Context, - ci *common.CompareInfo, + ci *CompareInfo, whitespaceBehavior git.TrustedCmdArgs, ) bool { var ( @@ -800,6 +810,7 @@ func CompareDiff(ctx *context.Context) { } ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID) + ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsDiffCompare"] = true _, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 97f2195116..afa6160d42 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -328,14 +328,14 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } - if ctx.IsSigned { - if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil { - ctx.ServerError("LoadIsRead", err) - return - } - } else { - for i := range issues { + // Get posters. + for i := range issues { + // Check read status + if !ctx.IsSigned { issues[i].IsRead = true + } else if err = issues[i].GetIsRead(ctx, ctx.Doer.ID); err != nil { + ctx.ServerError("GetIsRead", err) + return } } @@ -927,6 +927,7 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles } } } + } if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/ @@ -1601,20 +1602,20 @@ func ViewIssue(ctx *context.Context) { // Render comments and fetch participants. participants[0] = issue.Poster - - if err := issue.Comments.LoadAttachmentsByIssue(ctx); err != nil { - ctx.ServerError("LoadAttachmentsByIssue", err) - return - } - if err := issue.Comments.LoadPosters(ctx); err != nil { - ctx.ServerError("LoadPosters", err) - return - } - for _, comment = range issue.Comments { comment.Issue = issue + if err := comment.LoadPoster(ctx); err != nil { + ctx.ServerError("LoadPoster", err) + return + } + if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview { + if err := comment.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } + comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ Links: markup.Links{ Base: ctx.Repo.RepoLink, @@ -1662,6 +1663,7 @@ func ViewIssue(ctx *context.Context) { comment.Milestone = ghostMilestone } } else if comment.Type == issues_model.CommentTypeProject { + if err = comment.LoadProject(ctx); err != nil { ctx.ServerError("LoadProject", err) return @@ -1679,6 +1681,7 @@ func ViewIssue(ctx *context.Context) { if comment.ProjectID > 0 && comment.Project == nil { comment.Project = ghostProject } + } else if comment.Type == issues_model.CommentTypeAssignees || comment.Type == issues_model.CommentTypeReviewRequest { if err = comment.LoadAssigneeUserAndTeam(ctx); err != nil { ctx.ServerError("LoadAssigneeUserAndTeam", err) @@ -1726,6 +1729,10 @@ func ViewIssue(ctx *context.Context) { for _, codeComments := range comment.Review.CodeComments { for _, lineComments := range codeComments { for _, c := range lineComments { + if err := c.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } // Check tag. role, ok = marked[c.PosterID] if ok { @@ -2603,6 +2610,7 @@ func SearchIssues(ctx *context.Context) { var includedAnyLabels []int64 { + labels := ctx.FormTrim("labels") var includedLabelNames []string if len(labels) > 0 { @@ -2990,6 +2998,7 @@ func NewComment(ctx *context.Context) { if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) && (form.Status == "reopen" || form.Status == "close") && !(issue.IsPull && issue.PullRequest.HasMerged) { + // Duplication and conflict check should apply to reopen pull request. var pr *issues_model.PullRequest @@ -3156,6 +3165,12 @@ func UpdateCommentContent(ctx *context.Context) { oldContent := comment.Content comment.Content = ctx.FormString("content") + if len(comment.Content) == 0 { + ctx.JSON(http.StatusOK, map[string]any{ + "content": "", + }) + return + } if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { ctx.ServerError("UpdateComment", err) return diff --git a/routers/web/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go index 66b38688ec..e3b85ee638 100644 --- a/routers/web/repo/issue_dependency.go +++ b/routers/web/repo/issue_dependency.go @@ -109,7 +109,7 @@ func RemoveDependency(ctx *context.Context) { } // Dependency Type - depTypeStr := ctx.Req.PostFormValue("dependencyType") + depTypeStr := ctx.Req.PostForm.Get("dependencyType") var depType issues_model.DependencyType diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go index 5cff9f4ddd..6799bf8eb2 100644 --- a/routers/web/repo/issue_watch.go +++ b/routers/web/repo/issue_watch.go @@ -14,7 +14,7 @@ import ( ) const ( - tplWatching base.TplName = "repo/issue/view_content/sidebar/watching" + tplWatching base.TplName = "repo/issue/view_content/watching" ) // IssueWatch sets issue watching diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index a0dd36927f..c6c6142534 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -670,6 +670,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } if pb != nil && pb.EnableStatusCheck { + var missingRequiredChecks []string for _, requiredContext := range pb.StatusCheckContexts { contextFound := false @@ -872,6 +873,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi // Validate the given commit sha to show (if any passed) if willShowSpecifiedCommit || willShowSpecifiedCommitRange { + foundStartCommit := len(specifiedStartCommit) == 0 foundEndCommit := len(specifiedEndCommit) == 0 @@ -1183,6 +1185,7 @@ func UpdatePullRequest(ctx *context.Context) { ctx.Flash.Error(flashError) ctx.Redirect(issue.Link()) return + } ctx.Flash.Error(err.Error()) ctx.Redirect(issue.Link()) @@ -1436,6 +1439,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true + ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 24763668d0..afa3b17d31 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -169,9 +169,11 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori } ctx.Data["PageIsPullFiles"] = (origin == "diff") - if err := comments.LoadAttachments(ctx); err != nil { - ctx.ServerError("LoadAttachments", err) - return + for _, c := range comments { + if err := c.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } } ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled @@ -248,8 +250,6 @@ func SubmitReview(ctx *context.Context) { if issues_model.IsContentEmptyErr(err) { ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty")) ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index)) - } else if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { - ctx.Status(http.StatusUnprocessableEntity) } else { ctx.ServerError("SubmitReview", err) } @@ -304,6 +304,7 @@ func UpdateViewedFiles(ctx *context.Context) { updatedFiles := make(map[string]pull_model.ViewedState, len(data.Files)) for file, viewed := range data.Files { + // Only unviewed and viewed are possible, has-changed can not be set from the outside state := pull_model.Unviewed if viewed { diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 3927e3d2d9..908ce8138e 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -127,11 +127,6 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions) return nil, err } - err = r.LoadArchiveDownloadCount(ctx) - if err != nil { - return nil, err - } - if !r.IsDraft { if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil { return nil, err @@ -360,12 +355,6 @@ func SingleRelease(ctx *context.Context) { ctx.Data["Title"] = release.Title } - err = release.LoadArchiveDownloadCount(ctx) - if err != nil { - ctx.ServerError("LoadArchiveDownloadCount", err) - return - } - ctx.Data["Releases"] = releases ctx.HTML(http.StatusOK, tplReleasesList) } @@ -441,20 +430,6 @@ func NewRelease(ctx *context.Context) { } ctx.Data["Tags"] = tags - // We set the value of the hide_archive_link textbox depending on the latest release - latestRelease, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) - if err != nil { - if repo_model.IsErrReleaseNotExist(err) { - ctx.Data["hide_archive_links"] = false - } else { - ctx.ServerError("GetLatestReleaseByRepoID", err) - return - } - } - if latestRelease != nil { - ctx.Data["hide_archive_links"] = latestRelease.HideArchiveLinks - } - ctx.HTML(http.StatusOK, tplReleaseNew) } @@ -539,18 +514,17 @@ func NewReleasePost(ctx *context.Context) { } rel = &repo_model.Release{ - RepoID: ctx.Repo.Repository.ID, - Repo: ctx.Repo.Repository, - PublisherID: ctx.Doer.ID, - Publisher: ctx.Doer, - Title: form.Title, - TagName: form.TagName, - Target: form.Target, - Note: form.Content, - IsDraft: len(form.Draft) > 0, - IsPrerelease: form.Prerelease, - HideArchiveLinks: form.HideArchiveLinks, - IsTag: false, + RepoID: ctx.Repo.Repository.ID, + Repo: ctx.Repo.Repository, + PublisherID: ctx.Doer.ID, + Publisher: ctx.Doer, + Title: form.Title, + TagName: form.TagName, + Target: form.Target, + Note: form.Content, + IsDraft: len(form.Draft) > 0, + IsPrerelease: form.Prerelease, + IsTag: false, } if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil { @@ -580,7 +554,6 @@ func NewReleasePost(ctx *context.Context) { rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease rel.PublisherID = ctx.Doer.ID - rel.HideArchiveLinks = form.HideArchiveLinks rel.IsTag = false if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil, true); err != nil { @@ -618,7 +591,6 @@ func EditRelease(ctx *context.Context) { ctx.Data["title"] = rel.Title ctx.Data["content"] = rel.Note ctx.Data["prerelease"] = rel.IsPrerelease - ctx.Data["hide_archive_links"] = rel.HideArchiveLinks ctx.Data["IsDraft"] = rel.IsDraft rel.Repo = ctx.Repo.Repository @@ -665,7 +637,6 @@ func EditReleasePost(ctx *context.Context) { ctx.Data["title"] = rel.Title ctx.Data["content"] = rel.Note ctx.Data["prerelease"] = rel.IsPrerelease - ctx.Data["hide_archive_links"] = rel.HideArchiveLinks if ctx.HasError() { ctx.HTML(http.StatusOK, tplReleaseNew) @@ -691,7 +662,6 @@ func EditReleasePost(ctx *context.Context) { rel.Note = form.Content rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease - rel.HideArchiveLinks = form.HideArchiveLinks if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments, false); err != nil { ctx.ServerError("UpdateRelease", err) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index abd68630a4..b2458a561a 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -456,7 +456,7 @@ func RedirectDownload(ctx *context.Context) { // Download an archive of a repository func Download(ctx *context.Context) { uri := ctx.Params("*") - aReq, err := archiver_service.NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) if err != nil { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { ctx.Error(http.StatusBadRequest, err.Error()) @@ -480,25 +480,11 @@ func Download(ctx *context.Context) { func download(ctx *context.Context, archiveName string, archiver *repo_model.RepoArchiver) { downloadName := ctx.Repo.Repository.Name + "-" + archiveName - // Add nix format link header so tarballs lock correctly: - // https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md - ctx.Resp.Header().Add("Link", fmt.Sprintf("<%s/archive/%s.tar.gz?rev=%s>; rel=\"immutable\"", - ctx.Repo.Repository.APIURL(), - archiver.CommitID, archiver.CommitID)) - rPath := archiver.RelativePath() if setting.RepoArchive.Storage.MinioConfig.ServeDirect { // If we have a signed url (S3, object storage), redirect to this directly. u, err := storage.RepoArchives.URL(rPath, downloadName) if u != nil && err == nil { - if archiver.ReleaseID != 0 { - err = repo_model.CountArchiveDownload(ctx, ctx.Repo.Repository.ID, archiver.ReleaseID, archiver.Type) - if err != nil { - ctx.ServerError("CountArchiveDownload", err) - return - } - } - ctx.Redirect(u.String()) return } @@ -512,14 +498,6 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep } defer fr.Close() - if archiver.ReleaseID != 0 { - err = repo_model.CountArchiveDownload(ctx, ctx.Repo.Repository.ID, archiver.ReleaseID, archiver.Type) - if err != nil { - ctx.ServerError("CountArchiveDownload", err) - return - } - } - ctx.ServeContent(fr, &context.ServeHeaderOptions{ Filename: downloadName, LastModified: archiver.CreatedUnix.AsLocalTime(), @@ -531,7 +509,7 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep // kind of drop it on the floor if this is the case. func InitiateDownload(ctx *context.Context) { uri := ctx.Params("*") - aReq, err := archiver_service.NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri) if err != nil { ctx.ServerError("archiver_service.NewRequest", err) return diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 2644b27229..e98cd46c4e 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -352,6 +352,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { // or of directory if not in root directory. ctx.Data["LatestCommit"] = latestCommit if latestCommit != nil { + verification := asymkey_model.ParseCommitWithSignature(ctx, latestCommit) if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) { diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go index 79c03e4e8c..0f705399c9 100644 --- a/routers/web/shared/actions/variables.go +++ b/routers/web/shared/actions/variables.go @@ -4,13 +4,17 @@ package actions import ( + "errors" + "regexp" + "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/web" - actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" + secret_service "code.gitea.io/gitea/services/secrets" ) func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { @@ -25,16 +29,41 @@ func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { ctx.Data["Variables"] = variables } +// some regular expression of `variables` and `secrets` +// reference to: +// https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables +// https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets +var ( + forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI") +) + +func envNameCIRegexMatch(name string) error { + if forbiddenEnvNameCIRx.MatchString(name) { + log.Error("Env Name cannot be ci") + return errors.New("env name cannot be ci") + } + return nil +} + func CreateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) { form := web.GetForm(ctx).(*forms.EditVariableForm) - v, err := actions_service.CreateVariable(ctx, ownerID, repoID, form.Name, form.Data) - if err != nil { - log.Error("CreateVariable: %v", err) - ctx.JSONError(ctx.Tr("actions.variables.creation.failed")) + if err := secret_service.ValidateName(form.Name); err != nil { + ctx.JSONError(err.Error()) return } + if err := envNameCIRegexMatch(form.Name); err != nil { + ctx.JSONError(err.Error()) + return + } + + v, err := actions_model.InsertVariable(ctx, ownerID, repoID, form.Name, ReserveLineBreakForTextarea(form.Data)) + if err != nil { + log.Error("InsertVariable error: %v", err) + ctx.JSONError(ctx.Tr("actions.variables.creation.failed")) + return + } ctx.Flash.Success(ctx.Tr("actions.variables.creation.success", v.Name)) ctx.JSONRedirect(redirectURL) } @@ -43,8 +72,23 @@ func UpdateVariable(ctx *context.Context, redirectURL string) { id := ctx.ParamsInt64(":variable_id") form := web.GetForm(ctx).(*forms.EditVariableForm) - if ok, err := actions_service.UpdateVariable(ctx, id, form.Name, form.Data); err != nil || !ok { - log.Error("UpdateVariable: %v", err) + if err := secret_service.ValidateName(form.Name); err != nil { + ctx.JSONError(err.Error()) + return + } + + if err := envNameCIRegexMatch(form.Name); err != nil { + ctx.JSONError(err.Error()) + return + } + + ok, err := actions_model.UpdateVariable(ctx, &actions_model.ActionVariable{ + ID: id, + Name: strings.ToUpper(form.Name), + Data: ReserveLineBreakForTextarea(form.Data), + }) + if err != nil || !ok { + log.Error("UpdateVariable error: %v", err) ctx.JSONError(ctx.Tr("actions.variables.update.failed")) return } @@ -55,7 +99,7 @@ func UpdateVariable(ctx *context.Context, redirectURL string) { func DeleteVariable(ctx *context.Context, redirectURL string) { id := ctx.ParamsInt64(":variable_id") - if err := actions_service.DeleteVariableByID(ctx, id); err != nil { + if _, err := db.DeleteByBean(ctx, &actions_model.ActionVariable{ID: id}); err != nil { log.Error("Delete variable [%d] failed: %v", id, err) ctx.JSONError(ctx.Tr("actions.variables.deletion.failed")) return @@ -63,3 +107,12 @@ func DeleteVariable(ctx *context.Context, redirectURL string) { ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success")) ctx.JSONRedirect(redirectURL) } + +func ReserveLineBreakForTextarea(input string) string { + // Since the content is from a form which is a textarea, the line endings are \r\n. + // It's a standard behavior of HTML. + // But we want to store them as \n like what GitHub does. + // And users are unlikely to really need to keep the \r. + // Other than this, we should respect the original content, even leading or trailing spaces. + return strings.ReplaceAll(input, "\r\n", "\n") +} diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index 3bd421f86a..73505ec372 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -7,8 +7,8 @@ import ( "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/web/shared/actions" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" secret_service "code.gitea.io/gitea/services/secrets" @@ -27,7 +27,7 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { form := web.GetForm(ctx).(*forms.AddSecretForm) - s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data)) + s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, actions.ReserveLineBreakForTextarea(form.Data)) if err != nil { log.Error("CreateOrUpdateSecret failed: %v", err) ctx.JSONError(ctx.Tr("secrets.creation.failed")) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 1893e91221..2e4b9dae65 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -376,7 +376,7 @@ func Action(ctx *context.Context) { } if redirectViaJSON { - ctx.JSON(http.StatusOK, map[string]any{ + ctx.JSON(http.StatusOK, map[string]interface{}{ "redirect": ctx.ContextUser.HomeLink(), }) return diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 795ee59d58..6f412aed7f 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -243,7 +243,7 @@ func DeleteEmail(ctx *context.Context) { // DeleteAccount render user suicide page and response for delete user himself func DeleteAccount(ctx *context.Context) { - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureDeletion) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureDeletion) { ctx.Error(http.StatusNotFound) return } @@ -327,7 +327,7 @@ func loadAccountData(ctx *context.Context) { ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference ctx.Data["ActivationsPending"] = pendingActivation ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm - ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer) + ctx.Data["UserDisabledFeatures"] = &setting.Admin.UserDisabledFeatures if setting.Service.UserDeleteWithCommentsMaxTime != 0 { ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String() diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 9462be71c2..d2b60fc809 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -10,7 +10,6 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" @@ -79,7 +78,7 @@ func KeysPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.add_principal_success", form.Content)) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "gpg": - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageGPGKeys) { ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) return } @@ -160,7 +159,7 @@ func KeysPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.verify_gpg_key_success", keyID)) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "ssh": - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) return } @@ -204,7 +203,7 @@ func KeysPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title)) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "verify_ssh": - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) return } @@ -241,7 +240,7 @@ func KeysPost(ctx *context.Context) { func DeleteKey(ctx *context.Context) { switch ctx.FormString("type") { case "gpg": - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageGPGKeys) { ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) return } @@ -251,7 +250,7 @@ func DeleteKey(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success")) } case "ssh": - if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) return } @@ -334,5 +333,5 @@ func loadKeysData(ctx *context.Context) { ctx.Data["VerifyingID"] = ctx.FormString("verify_gpg") ctx.Data["VerifyingFingerprint"] = ctx.FormString("verify_ssh") - ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer) + ctx.Data["UserDisabledFeatures"] = &setting.Admin.UserDisabledFeatures } diff --git a/routers/web/web.go b/routers/web/web.go index 8faedca178..23695ee4db 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -654,7 +654,7 @@ func registerRoutes(m *web.Route) { m.Get("/system_status", admin.SystemStatus) m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost) - if setting.Database.Type.IsMySQL() { + if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() { m.Get("/self_check", admin.SelfCheck) } diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index 1f3de70db0..e4b2aacce8 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -64,51 +64,6 @@ func WebfingerQuery(ctx *context.Context) { if u != nil && u.KeepEmailPrivate { err = user_model.ErrUserNotExist{} } - case "https", "http": - if resource.Host != appURL.Host { - ctx.Error(http.StatusBadRequest) - return - } - - p := strings.Trim(resource.Path, "/") - if len(p) == 0 { - ctx.Error(http.StatusNotFound) - return - } - - parts := strings.Split(p, "/") - - switch len(parts) { - case 1: // user - u, err = user_model.GetUserByName(ctx, parts[0]) - case 2: // repository - ctx.Error(http.StatusNotFound) - return - - case 3: - switch parts[2] { - case "issues": - ctx.Error(http.StatusNotFound) - return - - case "pulls": - ctx.Error(http.StatusNotFound) - return - - case "projects": - ctx.Error(http.StatusNotFound) - return - - default: - ctx.Error(http.StatusNotFound) - return - } - - default: - ctx.Error(http.StatusNotFound) - return - } - default: ctx.Error(http.StatusBadRequest) return diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index 12db2bae56..f73ae8ae4c 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) { assert.Nil(t, err) assert.NotEqual(t, "", token) claims := jwt.MapClaims{} - _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { + _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { return setting.GetGeneralTokenSigningSecret(), nil }) assert.Nil(t, err) diff --git a/services/actions/interface.go b/services/actions/interface.go deleted file mode 100644 index d4fa782fec..0000000000 --- a/services/actions/interface.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package actions - -import "code.gitea.io/gitea/services/context" - -// API for actions of a repository or organization -type API interface { - // ListActionsSecrets list secrets - ListActionsSecrets(*context.APIContext) - // CreateOrUpdateSecret create or update a secret - CreateOrUpdateSecret(*context.APIContext) - // DeleteSecret delete a secret - DeleteSecret(*context.APIContext) - // ListVariables list variables - ListVariables(*context.APIContext) - // GetVariable get a variable - GetVariable(*context.APIContext) - // DeleteVariable delete a variable - DeleteVariable(*context.APIContext) - // CreateVariable create a variable - CreateVariable(*context.APIContext) - // UpdateVariable update a variable - UpdateVariable(*context.APIContext) - // GetRegistrationToken get registration token - GetRegistrationToken(*context.APIContext) -} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 78f413c214..365212d9c2 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -329,15 +329,13 @@ func handleWorkflows( TriggerEvent: dwf.TriggerEvent.Name, Status: actions_model.StatusWaiting, } - - need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer) - if err != nil { + if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil { log.Error("check if need approval for repo %d with user %d: %v", input.Repo.ID, input.Doer.ID, err) continue + } else { + run.NeedApproval = need } - run.NeedApproval = need - if err := run.LoadAttributes(ctx); err != nil { log.Error("LoadAttributes: %v", err) continue diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 18f3324fd2..e4e56e5122 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -132,14 +132,8 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) Status: actions_model.StatusWaiting, } - vars, err := actions_model.GetVariablesOfRun(ctx, run) - if err != nil { - log.Error("GetVariablesOfRun: %v", err) - return err - } - // Parse the workflow specification from the cron schedule - workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) + workflows, err := jobparser.Parse(cron.Content) if err != nil { return err } diff --git a/services/actions/variables.go b/services/actions/variables.go deleted file mode 100644 index 8dde9c4af5..0000000000 --- a/services/actions/variables.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package actions - -import ( - "context" - "regexp" - "strings" - - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" - secret_service "code.gitea.io/gitea/services/secrets" -) - -func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*actions_model.ActionVariable, error) { - if err := secret_service.ValidateName(name); err != nil { - return nil, err - } - - if err := envNameCIRegexMatch(name); err != nil { - return nil, err - } - - v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data)) - if err != nil { - return nil, err - } - - return v, nil -} - -func UpdateVariable(ctx context.Context, variableID int64, name, data string) (bool, error) { - if err := secret_service.ValidateName(name); err != nil { - return false, err - } - - if err := envNameCIRegexMatch(name); err != nil { - return false, err - } - - return actions_model.UpdateVariable(ctx, &actions_model.ActionVariable{ - ID: variableID, - Name: strings.ToUpper(name), - Data: util.ReserveLineBreakForTextarea(data), - }) -} - -func DeleteVariableByID(ctx context.Context, variableID int64) error { - return actions_model.DeleteVariable(ctx, variableID) -} - -func DeleteVariableByName(ctx context.Context, ownerID, repoID int64, name string) error { - if err := secret_service.ValidateName(name); err != nil { - return err - } - - if err := envNameCIRegexMatch(name); err != nil { - return err - } - - v, err := GetVariable(ctx, actions_model.FindVariablesOpts{ - OwnerID: ownerID, - RepoID: repoID, - Name: name, - }) - if err != nil { - return err - } - - return actions_model.DeleteVariable(ctx, v.ID) -} - -func GetVariable(ctx context.Context, opts actions_model.FindVariablesOpts) (*actions_model.ActionVariable, error) { - vars, err := actions_model.FindVariables(ctx, opts) - if err != nil { - return nil, err - } - if len(vars) != 1 { - return nil, util.NewNotExistErrorf("variable not found") - } - return vars[0], nil -} - -// some regular expression of `variables` and `secrets` -// reference to: -// https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables -// https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets -var ( - forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI") -) - -func envNameCIRegexMatch(name string) error { - if forbiddenEnvNameCIRx.MatchString(name) { - log.Error("Env Name cannot be ci") - return util.NewInvalidArgumentErrorf("env name cannot be ci") - } - return nil -} diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go index ba407b351a..dc4cb2c940 100644 --- a/services/auth/source/ldap/source.go +++ b/services/auth/source/ldap/source.go @@ -34,7 +34,6 @@ type Source struct { BindPassword string // Bind DN password UserBase string // Base search path for users UserDN string // Template for the DN of the user for simple auth - DefaultDomainName string // DomainName used if none are in the field, default "localhost.local" AttributeUsername string // Username attribute AttributeName string // First name attribute AttributeSurname string // Surname attribute diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 1f70edaa82..62f052d68c 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -105,11 +105,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } if len(su.Mail) == 0 { - domainName := source.DefaultDomainName - if len(domainName) == 0 { - domainName = "localhost.local" - } - su.Mail = fmt.Sprintf("%s@%s", su.Username, domainName) + su.Mail = fmt.Sprintf("%s@localhost.local", su.Username) } fullName := composeFullName(su.Name, su.Surname, su.Username) @@ -159,6 +155,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { !strings.EqualFold(usr.Email, su.Mail) || usr.FullName != fullName || !usr.IsActive { + log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name) opts := &user_service.UpdateOptions{ diff --git a/services/auth/source/oauth2/source_callout.go b/services/auth/source/oauth2/source_callout.go index f95a80fc19..8d70bee248 100644 --- a/services/auth/source/oauth2/source_callout.go +++ b/services/auth/source/oauth2/source_callout.go @@ -5,25 +5,16 @@ package oauth2 import ( "net/http" - "net/url" "github.com/markbates/goth" "github.com/markbates/goth/gothic" ) // Callout redirects request/response pair to authenticate against the provider -func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error { +func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { // not sure if goth is thread safe (?) when using multiple providers request.Header.Set(ProviderHeaderKey, source.authSource.Name) - var querySuffix string - if codeChallengeS256 != "" { - querySuffix = "&" + url.Values{ - "code_challenge_method": []string{"S256"}, - "code_challenge": []string{codeChallengeS256}, - }.Encode() - } - // don't use the default gothic begin handler to prevent issues when some error occurs // normally the gothic library will write some custom stuff to the response instead of our own nice error page // gothic.BeginAuthHandler(response, request) @@ -33,29 +24,17 @@ func (source *Source) Callout(request *http.Request, response http.ResponseWrite url, err := gothic.GetAuthURL(response, request) if err == nil { - // hacky way to set the code_challenge, but no better way until - // https://github.com/markbates/goth/issues/516 is resolved - http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect) + http.Redirect(response, request, url, http.StatusTemporaryRedirect) } return err } // Callback handles OAuth callback, resolve to a goth user and send back to original url // this will trigger a new authentication request, but because we save it in the session we can use that -func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) { +func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { // not sure if goth is thread safe (?) when using multiple providers request.Header.Set(ProviderHeaderKey, source.authSource.Name) - if codeVerifier != "" { - // hacky way to set the code_verifier... - // Will be picked up inside CompleteUserAuth: params := req.URL.Query() - // https://github.com/markbates/goth/pull/474/files - request = request.Clone(request.Context()) - q := request.URL.Query() - q.Add("code_verifier", codeVerifier) - request.URL.RawQuery = q.Encode() - } - gothRWMutex.RLock() defer gothRWMutex.RUnlock() diff --git a/services/auth/source/remote/source.go b/services/auth/source/remote/source.go deleted file mode 100644 index 4165858a56..0000000000 --- a/services/auth/source/remote/source.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Earl Warren -// SPDX-License-Identifier: MIT - -package remote - -import ( - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/json" -) - -type Source struct { - URL string - MatchingSource string - - // reference to the authSource - authSource *auth.Source -} - -func (source *Source) FromDB(bs []byte) error { - return json.UnmarshalHandleDoubleEncode(bs, &source) -} - -func (source *Source) ToDB() ([]byte, error) { - return json.Marshal(source) -} - -func (source *Source) SetAuthSource(authSource *auth.Source) { - source.authSource = authSource -} - -func init() { - auth.RegisterTypeConfig(auth.Remote, &Source{}) -} diff --git a/services/context/context_response.go b/services/context/context_response.go index 2f2d7b0e1b..372b4cb38b 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -44,10 +44,8 @@ func RedirectToUser(ctx *Base, userName string, redirectUserID int64) { ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect) } -// RedirectToFirst redirects to first not empty URL which likely belongs to current site. -// If no suitable redirection is found, it redirects to the home. -// It returns the location it redirected to. -func (ctx *Context) RedirectToFirst(location ...string) string { +// RedirectToFirst redirects to first not empty URL +func (ctx *Context) RedirectToFirst(location ...string) { for _, loc := range location { if len(loc) == 0 { continue @@ -58,11 +56,10 @@ func (ctx *Context) RedirectToFirst(location ...string) string { } ctx.Redirect(loc) - return loc + return } ctx.Redirect(setting.AppSubURL + "/") - return setting.AppSubURL + "/" } const tplStatus500 base.TplName = "status/500" diff --git a/services/context/repo.go b/services/context/repo.go index 3e30f2ba97..43eeab8098 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -848,6 +848,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { case RepoRefBranch: ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist) if len(ref) == 0 { + // check if ref is HEAD parts := strings.Split(path, "/") if parts[0] == headRefName { @@ -990,6 +991,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() + } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) { ctx.Repo.IsViewTag = true ctx.Repo.TagName = refName diff --git a/services/convert/convert.go b/services/convert/convert.go index 55996d8fe3..70ca5da2ec 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -185,13 +185,12 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo // ToTag convert a git.Tag to an api.Tag func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag { return &api.Tag{ - Name: t.Name, - Message: strings.TrimSpace(t.Message), - ID: t.ID.String(), - Commit: ToCommitMeta(repo, t), - ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"), - TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"), - ArchiveDownloadCount: t.ArchiveDownloadCount, + Name: t.Name, + Message: strings.TrimSpace(t.Message), + ID: t.ID.String(), + Commit: ToCommitMeta(repo, t), + ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"), + TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"), } } @@ -364,14 +363,13 @@ func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([] // ToAnnotatedTag convert git.Tag to api.AnnotatedTag func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag { return &api.AnnotatedTag{ - Tag: t.Name, - SHA: t.ID.String(), - Object: ToAnnotatedTagObject(repo, c), - Message: t.Message, - URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()), - Tagger: ToCommitUser(t.Tagger), - Verification: ToVerification(ctx, c), - ArchiveDownloadCount: t.ArchiveDownloadCount, + Tag: t.Name, + SHA: t.ID.String(), + Object: ToAnnotatedTagObject(repo, c), + Message: t.Message, + URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()), + Tagger: ToCommitUser(t.Tagger), + Verification: ToVerification(ctx, c), } } diff --git a/services/convert/release.go b/services/convert/release.go index 8c0f61b56c..bfff53e62f 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -13,23 +13,21 @@ import ( // ToAPIRelease convert a repo_model.Release to api.Release func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_model.Release) *api.Release { return &api.Release{ - ID: r.ID, - TagName: r.TagName, - Target: r.Target, - Title: r.Title, - Note: r.Note, - URL: r.APIURL(), - HTMLURL: r.HTMLURL(), - TarURL: r.TarURL(), - ZipURL: r.ZipURL(), - HideArchiveLinks: r.HideArchiveLinks, - UploadURL: r.APIUploadURL(), - IsDraft: r.IsDraft, - IsPrerelease: r.IsPrerelease, - CreatedAt: r.CreatedUnix.AsTime(), - PublishedAt: r.CreatedUnix.AsTime(), - Publisher: ToUser(ctx, r.Publisher, nil), - Attachments: ToAPIAttachments(repo, r.Attachments), - ArchiveDownloadCount: r.ArchiveDownloadCount, + ID: r.ID, + TagName: r.TagName, + Target: r.Target, + Title: r.Title, + Note: r.Note, + URL: r.APIURL(), + HTMLURL: r.HTMLURL(), + TarURL: r.TarURL(), + ZipURL: r.ZipURL(), + UploadURL: r.APIUploadURL(), + IsDraft: r.IsDraft, + IsPrerelease: r.IsPrerelease, + CreatedAt: r.CreatedUnix.AsTime(), + PublishedAt: r.CreatedUnix.AsTime(), + Publisher: ToUser(ctx, r.Publisher, nil), + Attachments: ToAPIAttachments(repo, r.Attachments), } } diff --git a/services/convert/repository.go b/services/convert/repository.go index 35becd96d0..7b7f944d79 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -77,13 +77,9 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } } hasWiki := false - globallyEditableWiki := false var externalWiki *api.ExternalWiki - if wikiUnit, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { + if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { hasWiki = true - if wikiUnit.DefaultPermissions == repo_model.UnitAccessModeWrite { - globallyEditableWiki = true - } } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil { hasWiki = true config := unit.ExternalWikiConfig() @@ -215,7 +211,6 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR InternalTracker: internalTracker, HasWiki: hasWiki, WikiBranch: repo.WikiBranch, - GloballyEditableWiki: globallyEditableWiki, HasProjects: hasProjects, HasReleases: hasReleases, HasPackages: hasPackages, diff --git a/services/convert/user.go b/services/convert/user.go index 789bc51097..98db53705b 100644 --- a/services/convert/user.go +++ b/services/convert/user.go @@ -76,7 +76,6 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap if authed { result.IsAdmin = user.IsAdmin result.LoginName = user.LoginName - result.SourceID = user.LoginSource result.LastLogin = user.LastLoginUnix.AsTime() result.Language = user.Language result.IsActive = user.IsActive diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go index 6b931b7036..0903ecc2a6 100644 --- a/services/doctor/dbconsistency.go +++ b/services/doctor/dbconsistency.go @@ -159,12 +159,6 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er Fixer: actions_model.FixRunnersWithoutBelongingOwner, FixedMessage: "Removed", }, - { - Name: "Action Runners without existing repository", - Counter: actions_model.CountRunnersWithoutBelongingRepo, - Fixer: actions_model.FixRunnersWithoutBelongingRepo, - FixedMessage: "Removed", - }, { Name: "Topics with empty repository count", Counter: repo_model.CountOrphanedTopics, @@ -233,9 +227,6 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er // find redirects without existing user. genericOrphanCheck("Orphaned Redirects without existing redirect user", "user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"), - // find archive download count without existing release - genericOrphanCheck("Archive download count without existing Release", - "repo_archive_download_count", "release", "repo_archive_download_count.release_id=release.id"), ) for _, c := range consistencyChecks { diff --git a/services/doctor/packages_nuget.go b/services/doctor/packages_nuget.go index 47fdb3ac12..8c0a2d856d 100644 --- a/services/doctor/packages_nuget.go +++ b/services/doctor/packages_nuget.go @@ -51,6 +51,7 @@ func PackagesNugetNuspecCheck(ctx context.Context, logger log.Logger, autofix bo logger.Info("Found %d versions for package %s", len(pvs), pkg.Name) for _, pv := range pvs { + pfs, err := packages.GetFilesByVersionID(ctx, pv.ID) if err != nil { logger.Error("Failed to get files for package version %s %s: %v", pkg.Name, pv.Version, err) diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index a3eca9473b..c9f3182b3a 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -26,7 +26,6 @@ type AuthenticationForm struct { AttributeUsername string AttributeName string AttributeSurname string - DefaultDomainName string AttributeMail string AttributeSSHPublicKey string AttributeAvatar string diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e4fcf8e0c0..e0540852af 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -559,16 +559,15 @@ type UpdateAllowEditsForm struct { // NewReleaseForm form for creating release type NewReleaseForm struct { - TagName string `binding:"Required;GitRefName;MaxSize(255)"` - Target string `form:"tag_target" binding:"Required;MaxSize(255)"` - Title string `binding:"MaxSize(255)"` - Content string - Draft string - TagOnly string - Prerelease bool - AddTagMsg bool - HideArchiveLinks bool - Files []string + TagName string `binding:"Required;GitRefName;MaxSize(255)"` + Target string `form:"tag_target" binding:"Required;MaxSize(255)"` + Title string `binding:"MaxSize(255)"` + Content string + Draft string + TagOnly string + Prerelease bool + AddTagMsg bool + Files []string } // Validate validates the fields @@ -579,12 +578,11 @@ func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) bindin // EditReleaseForm form for changing release type EditReleaseForm struct { - Title string `form:"title" binding:"Required;MaxSize(255)"` - Content string `form:"content"` - Draft string `form:"draft"` - Prerelease bool `form:"prerelease"` - HideArchiveLinks bool - Files []string + Title string `form:"title" binding:"Required;MaxSize(255)"` + Content string `form:"content"` + Draft string `form:"draft"` + Prerelease bool `form:"prerelease"` + Files []string } // Validate validates the fields diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index d9dbeedee5..0c89839edf 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1040,8 +1040,8 @@ func createDiffFile(diff *Diff, line string) *DiffFile { // diff --git a/b b/b b/b b/b b/b b/b // midpoint := (len(line) + len(cmdDiffHead) - 1) / 2 - newl, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:] - if len(newl) > 2 && len(old) > 2 && newl[2:] == old[2:] { + new, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:] + if len(new) > 2 && len(old) > 2 && new[2:] == old[2:] { curFile.OldName = old[2:] curFile.Name = old[2:] } diff --git a/services/issue/commit.go b/services/issue/commit.go index 8b927d52b6..e493a03211 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -117,6 +117,7 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m var refIssue *issues_model.Issue var err error for _, ref := range references.FindAllIssueReferences(c.Message) { + // issue is from another repo if len(ref.Owner) > 0 && len(ref.Name) > 0 { refRepo, err = repo_model.GetRepositoryByOwnerAndName(ctx, ref.Owner, ref.Name) @@ -184,15 +185,15 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m continue } } - isClosed := ref.Action == references.XRefActionCloses - if isClosed && len(ref.TimeLog) > 0 { + close := ref.Action == references.XRefActionCloses + if close && len(ref.TimeLog) > 0 { if err := issueAddTime(ctx, refIssue, doer, c.Timestamp, ref.TimeLog); err != nil { return err } } - if isClosed != refIssue.IsClosed { + if close != refIssue.IsClosed { refIssue.Repo = refRepo - if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, isClosed); err != nil { + if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, close); err != nil { return err } } diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index 272bf02e11..d402a238f2 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -410,6 +410,7 @@ func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, err return nil, false, fmt.Errorf("error while listing issues: %w", err) } for _, issue := range issues { + labels := make([]*base.Label, 0, len(issue.Labels)) for i := range issue.Labels { labels = append(labels, g.convertGiteaLabel(issue.Labels[i])) diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 065b687fa6..bbc44e958a 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -421,6 +421,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er return nil, false, fmt.Errorf("error while listing issues: %w", err) } for _, issue := range issues { + labels := make([]*base.Label, 0, len(issue.Labels)) for _, l := range issue.Labels { labels = append(labels, &base.Label{ diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 0270f87039..72e545581a 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -40,7 +40,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { } log.Trace("Doing: Update") - handler := func(bean any) error { + handler := func(idx int, bean any) error { var repo *repo_model.Repository var mirrorType SyncType var referenceID int64 @@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pullMirrorsRequested := 0 if pullLimit != 0 { if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error { - if err := handler(bean); err != nil { + if err := handler(idx, bean); err != nil { return err } pullMirrorsRequested++ @@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pushMirrorsRequested := 0 if pushLimit != 0 { if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error { - if err := handler(bean); err != nil { + if err := handler(idx, bean); err != nil { return err } pushMirrorsRequested++ diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 9f7ffb29c9..fa23986c54 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -466,7 +466,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) if len(results) > 0 { - if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok { + if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) return false } @@ -523,13 +523,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] } - newCommit, err := gitRepo.GetCommit(newCommitID) - if err != nil { + if newCommit, err := gitRepo.GetCommit(newCommitID); err != nil { log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err) continue + } else { + theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit) } - theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit) theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ @@ -557,6 +557,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) return false } + } log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) @@ -564,7 +565,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return true } -func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool { +func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool { if !m.Repo.IsEmpty { return true } diff --git a/services/pull/merge.go b/services/pull/merge.go index 1d6431ab66..2989d77c6a 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -237,9 +237,9 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U if err = ref.Issue.LoadRepo(ctx); err != nil { return err } - isClosed := ref.RefAction == references.XRefActionCloses - if isClosed != ref.Issue.IsClosed { - if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, isClosed); err != nil { + close := ref.RefAction == references.XRefActionCloses + if close != ref.Issue.IsClosed { + if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, close); err != nil { // Allow ErrDependenciesLeft if !issues_model.IsErrDependenciesLeft(err) { return err diff --git a/services/pull/pull.go b/services/pull/pull.go index 5fa426483f..720efdb0cb 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -803,6 +803,7 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ if err != nil { log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) return "" + } if len(commits) == 0 { break diff --git a/services/pull/review.go b/services/pull/review.go index cff6f346ae..6ad931b679 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -6,7 +6,6 @@ package pull import ( "context" - "errors" "fmt" "io" "regexp" @@ -44,9 +43,6 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error { return util.ErrPermissionDenied } -// ErrSubmitReviewOnClosedPR represents an error when an user tries to submit an approve or reject review associated to a closed or merged PR. -var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR") - // checkInvalidation checks if the line of code comment got changed by another commit. // If the line got changed the comment is going to be invalidated. func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { @@ -297,10 +293,6 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject { stale = false } else { - if issue.IsClosed { - return nil, nil, ErrSubmitReviewOnClosedPR - } - headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) if err != nil { return nil, nil, err diff --git a/services/release/release.go b/services/release/release.go index 5062af1436..c349ab5143 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -318,11 +318,6 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } } - err = repo_model.DeleteArchiveDownloadCountForRelease(ctx, rel.ID) - if err != nil { - return err - } - if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName). SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)). RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { diff --git a/services/remote/promote.go b/services/remote/promote.go deleted file mode 100644 index 6709b4cc1d..0000000000 --- a/services/remote/promote.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Earl Warren -// SPDX-License-Identifier: MIT - -package remote - -import ( - "context" - - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/auth/source/oauth2" - remote_source "code.gitea.io/gitea/services/auth/source/remote" -) - -type Reason int - -const ( - ReasonNoMatch Reason = iota - ReasonNotAuth2 - ReasonBadAuth2 - ReasonLoginNameNotExists - ReasonNotRemote - ReasonEmailIsSet - ReasonNoSource - ReasonSourceWrongType - ReasonCanPromote - ReasonPromoted - ReasonUpdateFail - ReasonErrorLoginName - ReasonErrorGetSource -) - -func NewReason(level log.Level, reason Reason, message string, args ...any) Reason { - log.Log(1, level, message, args...) - return reason -} - -func getUsersByLoginName(ctx context.Context, name string) ([]*user_model.User, error) { - if len(name) == 0 { - return nil, user_model.ErrUserNotExist{Name: name} - } - - users := make([]*user_model.User, 0, 5) - - return users, db.GetEngine(ctx). - Table("user"). - Where("login_name = ? AND login_type = ? AND type = ?", name, auth_model.Remote, user_model.UserTypeRemoteUser). - Find(&users) -} - -// The remote user has: -// -// Type UserTypeRemoteUser -// LogingType Remote -// LoginName set to the unique identifier of the originating authentication source -// LoginSource set to the Remote source that can be matched against an OAuth2 source -// -// If the source from which an authentification happens is OAuth2, an existing -// remote user will be promoted to an OAuth2 user provided: -// -// user.LoginName is the same as goth.UserID (argument loginName) -// user.LoginSource has a MatchingSource equals to the name of the OAuth2 provider -// -// Once promoted, the user will be logged in without further interaction from the -// user and will own all repositories, issues, etc. associated with it. -func MaybePromoteRemoteUser(ctx context.Context, source *auth_model.Source, loginName, email string) (promoted bool, reason Reason, err error) { - user, reason, err := getRemoteUserToPromote(ctx, source, loginName, email) - if err != nil || user == nil { - return false, reason, err - } - promote := &user_model.User{ - ID: user.ID, - Type: user_model.UserTypeIndividual, - Email: email, - LoginSource: source.ID, - LoginType: source.Type, - } - reason = NewReason(log.DEBUG, ReasonPromoted, "promote user %v: LoginName %v => %v, LoginSource %v => %v, LoginType %v => %v, Email %v => %v", user.ID, user.LoginName, promote.LoginName, user.LoginSource, promote.LoginSource, user.LoginType, promote.LoginType, user.Email, promote.Email) - if err := user_model.UpdateUserCols(ctx, promote, "type", "email", "login_source", "login_type"); err != nil { - return false, ReasonUpdateFail, err - } - return true, reason, nil -} - -func getRemoteUserToPromote(ctx context.Context, source *auth_model.Source, loginName, email string) (*user_model.User, Reason, error) { - if !source.IsOAuth2() { - return nil, NewReason(log.DEBUG, ReasonNotAuth2, "source %v is not OAuth2", source), nil - } - oauth2Source, ok := source.Cfg.(*oauth2.Source) - if !ok { - return nil, NewReason(log.ERROR, ReasonBadAuth2, "source claims to be OAuth2 but is not"), nil - } - - users, err := getUsersByLoginName(ctx, loginName) - if err != nil { - return nil, NewReason(log.ERROR, ReasonErrorLoginName, "getUserByLoginName('%s') %v", loginName, err), err - } - if len(users) == 0 { - return nil, NewReason(log.ERROR, ReasonLoginNameNotExists, "no user with LoginType UserTypeRemoteUser and LoginName '%s'", loginName), nil - } - - reason := ReasonNoSource - for _, u := range users { - userSource, err := auth_model.GetSourceByID(ctx, u.LoginSource) - if err != nil { - if auth_model.IsErrSourceNotExist(err) { - reason = NewReason(log.DEBUG, ReasonNoSource, "source id = %v for user %v not found %v", u.LoginSource, u.ID, err) - continue - } - return nil, NewReason(log.ERROR, ReasonErrorGetSource, "GetSourceByID('%s') %v", u.LoginSource, err), err - } - if u.Email != "" { - reason = NewReason(log.DEBUG, ReasonEmailIsSet, "the user email is already set to '%s'", u.Email) - continue - } - remoteSource, ok := userSource.Cfg.(*remote_source.Source) - if !ok { - reason = NewReason(log.DEBUG, ReasonSourceWrongType, "expected a remote source but got %T %v", userSource, userSource) - continue - } - - if oauth2Source.Provider != remoteSource.MatchingSource { - reason = NewReason(log.DEBUG, ReasonNoMatch, "skip OAuth2 source %s because it is different from %s which is the expected match for the remote source %s", oauth2Source.Provider, remoteSource.MatchingSource, remoteSource.URL) - continue - } - - return u, ReasonCanPromote, nil - } - - return nil, reason, nil -} diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 31e3e581b3..b337eac38a 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -357,6 +357,7 @@ func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListO return err } repoNamesToCheck = repoNamesToCheck[:0] + } return filepath.SkipDir }); err != nil { diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index c74712b4ba..01c58f0ce4 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -30,11 +30,10 @@ import ( // This is entirely opaque to external entities, though, and mostly used as a // handle elsewhere. type ArchiveRequest struct { - RepoID int64 - refName string - Type git.ArchiveType - CommitID string - ReleaseID int64 + RepoID int64 + refName string + Type git.ArchiveType + CommitID string } // ErrUnknownArchiveFormat request archive format is not supported @@ -71,7 +70,7 @@ func (e RepoRefNotFoundError) Is(err error) bool { // NewRequest creates an archival request, based on the URI. The // resulting ArchiveRequest is suitable for being passed to ArchiveRepository() // if it's determined that the request still needs to be satisfied. -func NewRequest(ctx context.Context, repoID int64, repo *git.Repository, uri string) (*ArchiveRequest, error) { +func NewRequest(repoID int64, repo *git.Repository, uri string) (*ArchiveRequest, error) { r := &ArchiveRequest{ RepoID: repoID, } @@ -100,17 +99,6 @@ func NewRequest(ctx context.Context, repoID int64, repo *git.Repository, uri str } r.CommitID = commitID.String() - - release, err := repo_model.GetRelease(ctx, repoID, r.refName) - if err != nil { - if !repo_model.IsErrReleaseNotExist(err) { - return nil, err - } - } - if release != nil { - r.ReleaseID = release.ID - } - return r, nil } @@ -132,10 +120,6 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver return nil, fmt.Errorf("models.GetRepoArchiver: %w", err) } - if archiver != nil { - archiver.ReleaseID = aReq.ReleaseID - } - if archiver != nil && archiver.Status == repo_model.ArchiverReady { // Archive already generated, we're done. return archiver, nil @@ -161,7 +145,6 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver return nil, fmt.Errorf("repo_model.GetRepoArchiver: %w", err) } if archiver != nil && archiver.Status == repo_model.ArchiverReady { - archiver.ReleaseID = aReq.ReleaseID return archiver, nil } } diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go index dbd4d9b3c7..ec6e9dfac3 100644 --- a/services/repository/archiver/archiver_test.go +++ b/services/repository/archiver/archiver_test.go @@ -31,47 +31,47 @@ func TestArchive_Basic(t *testing.T) { contexttest.LoadGitRepo(t, ctx) defer ctx.Repo.GitRepo.Close() - bogusReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") + bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName()) // Check a series of bogus requests. // Step 1, valid commit with a bad extension. - bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".dilbert") + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".dilbert") assert.Error(t, err) assert.Nil(t, bogusReq) // Step 2, missing commit. - bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip") + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip") assert.Error(t, err) assert.Nil(t, bogusReq) // Step 3, doesn't look like branch/tag/commit. - bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip") + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip") assert.Error(t, err) assert.Nil(t, bogusReq) - bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip") + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName()) - bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip") + bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip") assert.NoError(t, err) assert.NotNil(t, bogusReq) assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName()) // Now two valid requests, firstCommit with valid extensions. - zipReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") + zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) assert.NotNil(t, zipReq) - tgzReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz") + tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz") assert.NoError(t, err) assert.NotNil(t, tgzReq) - secondReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".zip") + secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".zip") assert.NoError(t, err) assert.NotNil(t, secondReq) @@ -91,7 +91,7 @@ func TestArchive_Basic(t *testing.T) { // Sleep two seconds to make sure the queue doesn't change. time.Sleep(2 * time.Second) - zipReq2, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") + zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) // This zipReq should match what's sitting in the queue, as we haven't // let it release yet. From the consumer's point of view, this looks like @@ -106,12 +106,12 @@ func TestArchive_Basic(t *testing.T) { // Now we'll submit a request and TimedWaitForCompletion twice, before and // after we release it. We should trigger both the timeout and non-timeout // cases. - timedReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz") + timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz") assert.NoError(t, err) assert.NotNil(t, timedReq) ArchiveRepository(db.DefaultContext, timedReq) - zipReq2, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") + zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip") assert.NoError(t, err) // Now, we're guaranteed to have released the original zipReq from the queue. // Ensure that we don't get handed back the released entry somehow, but they diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 1944f11f03..04a0ea0b38 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -7,7 +7,6 @@ import ( "context" "crypto/sha256" "fmt" - "slices" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -90,19 +89,13 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato sha = commit.ID.String() } - if err := db.WithTx(ctx, func(ctx context.Context) error { - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: commit.ID, - CommitStatus: status, - }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) - } - - return git_model.UpdateCommitStatusSummary(ctx, repo.ID, commit.ID.String()) + if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ + Repo: repo, + Creator: creator, + SHA: commit.ID, + CommitStatus: status, }); err != nil { - return err + return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) } defaultBranchCommit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) @@ -154,35 +147,8 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep return nil, fmt.Errorf("FindBranchesByRepoAndBranchName: %v", err) } - var repoSHAs []git_model.RepoSHA - for id, sha := range repoIDsToLatestCommitSHAs { - repoSHAs = append(repoSHAs, git_model.RepoSHA{RepoID: id, SHA: sha}) - } - - summaryResults, err := git_model.GetLatestCommitStatusForRepoAndSHAs(ctx, repoSHAs) - if err != nil { - return nil, fmt.Errorf("GetLatestCommitStatusForRepoAndSHAs: %v", err) - } - - for _, summary := range summaryResults { - for i, repo := range repos { - if repo.ID == summary.RepoID { - results[i] = summary - _ = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool { - return repoSHA.RepoID == repo.ID - }) - if results[i].State != "" { - if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil { - log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) - } - } - break - } - } - } - // call the database O(1) times to get the commit statuses for all repos - repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoSHAs) + repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll) if err != nil { return nil, fmt.Errorf("GetLatestCommitStatusForPairs: %v", err) } diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 7241d3d655..516e7f1475 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -194,6 +194,7 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int Stats: &commitStats, } extendedCommitStats = append(extendedCommitStats, res) + } _ = stdoutReader.Close() return nil diff --git a/services/repository/delete.go b/services/repository/delete.go index 21d9ebcf0b..d5a8849033 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -162,7 +162,6 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID &actions_model.ActionScheduleSpec{RepoID: repoID}, &actions_model.ActionSchedule{RepoID: repoID}, &actions_model.ActionArtifact{RepoID: repoID}, - &repo_model.RepoArchiveDownloadCount{RepoID: repoID}, &actions_model.ActionRunnerToken{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 81a61da5ed..e677949e86 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -208,6 +208,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %w", err) } opts.LastCommitID = lastCommitID.String() + } for _, file := range opts.Files { @@ -359,6 +360,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep Path: file.Options.treePath, } } + } } diff --git a/services/user/delete.go b/services/user/delete.go index 74dbc09b82..e890990994 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -106,6 +106,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) if purge || (setting.Service.UserDeleteWithCommentsMaxTime != 0 && u.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())) { + // Delete Comments const batchSize = 50 for { diff --git a/services/user/update_test.go b/services/user/update_test.go index c2ff26a140..7ed764b539 100644 --- a/services/user/update_test.go +++ b/services/user/update_test.go @@ -94,7 +94,7 @@ func TestUpdateAuth(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28}) - userCopy := *user + copy := *user assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{ LoginName: optional.Some("new-login"), @@ -106,8 +106,8 @@ func TestUpdateAuth(t *testing.T) { MustChangePassword: optional.Some(true), })) assert.True(t, user.MustChangePassword) - assert.NotEqual(t, userCopy.Passwd, user.Passwd) - assert.NotEqual(t, userCopy.Salt, user.Salt) + assert.NotEqual(t, copy.Passwd, user.Passwd) + assert.NotEqual(t, copy.Salt, user.Salt) assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{ ProhibitLogin: optional.Some(true), diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 8db1c72572..d488b1725a 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -78,13 +78,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { } if authorization != "" { req.Header.Set("Authorization", authorization) - redacted := "******" - if strings.HasPrefix(authorization, "Bearer ") { - redacted = "Bearer " + redacted - } else if strings.HasPrefix(authorization, "Basic ") { - redacted = "Basic " + redacted - } - t.RequestInfo.Headers["Authorization"] = redacted + t.RequestInfo.Headers["Authorization"] = "******" } t.ResponseInfo = &webhook_model.HookResponse{ diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 0311d810e6..c689332666 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -132,7 +132,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { } assert.True(t, hookTask.IsSucceed) - assert.Equal(t, "Bearer ******", hookTask.RequestInfo.Headers["Authorization"]) + assert.Equal(t, "******", hookTask.RequestInfo.Headers["Authorization"]) } func TestWebhookDeliverHookTask(t *testing.T) { @@ -152,6 +152,7 @@ func TestWebhookDeliverHookTask(t *testing.T) { case "/webhook/6db5dc1e282529a8c162c7fe93dd2667494eeb51": // Version 2 + assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) assert.Equal(t, "application/json", r.Header.Get("Content-Type")) body, err := io.ReadAll(r.Body) assert.NoError(t, err) diff --git a/services/webhook/discord.go b/services/webhook/discord.go index 80f6cfb79b..cb756688c8 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -304,12 +304,14 @@ func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) { switch event { + case webhook_module.HookEventPullRequestReviewApproved: return "approved", nil case webhook_module.HookEventPullRequestReviewRejected: return "rejected", nil case webhook_module.HookEventPullRequestReviewComment: return "comment", nil + default: return "", errors.New("unknown event type") } diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 06176e8dd3..697e33e94c 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -42,15 +42,16 @@ func (matrixHandler) UnmarshalForm(bind func(any)) forms.WebhookForm { HomeserverURL string `binding:"Required;ValidUrl"` RoomID string `binding:"Required"` MessageType int - AccessToken string `binding:"Required"` + + // enforce requirement of authorization_header + // (value will still be set in the embedded WebhookCoreForm) + AuthorizationHeader string `binding:"Required"` } bind(&form) - form.AuthorizationHeader = "Bearer " + strings.TrimSpace(form.AccessToken) - // https://spec.matrix.org/v1.10/client-server-api/#sending-events-to-a-room return forms.WebhookForm{ WebhookCoreForm: form.WebhookCoreForm, - URL: fmt.Sprintf("%s/_matrix/client/v3/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), + URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), ContentType: webhook_model.ContentTypeJSON, Secret: "", HTTPMethod: http.MethodPut, @@ -90,7 +91,7 @@ func (matrixHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t } req.Header.Set("Content-Type", "application/json") - return req, body, nil + return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially } const matrixPayloadSizeLimit = 1024 * 64 @@ -217,6 +218,7 @@ func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) { if i < len(p.Commits)-1 { text += "
    " } + } return m.newPayload(text, p.Commits...) diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 7031a45bec..0269ccccea 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -201,7 +201,7 @@ func TestMatrixJSONPayload(t *testing.T) { RepoID: 3, IsActive: true, Type: webhook_module.MATRIX, - URL: "https://matrix.example.com/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message", + URL: "https://matrix.example.com/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message", Meta: `{"message_type":0}`, // text } task := &webhook_model.HookTask{ @@ -217,7 +217,8 @@ func TestMatrixJSONPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "PUT", req.Method) - assert.Equal(t, "/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) + assert.Equal(t, "/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) assert.Equal(t, "application/json", req.Header.Get("Content-Type")) var body MatrixPayload err = json.NewDecoder(req.Body).Decode(&body) diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go index e7501110a2..1561b9e6e6 100644 --- a/services/webhook/sourcehut/builds.go +++ b/services/webhook/sourcehut/builds.go @@ -49,7 +49,6 @@ type buildsForm struct { ManifestPath string `binding:"Required"` Visibility string `binding:"Required;In(PUBLIC,UNLISTED,PRIVATE)"` Secrets bool - AccessToken string `binding:"Required"` } var _ binding.Validator = &buildsForm{} @@ -64,7 +63,13 @@ func (f *buildsForm) Validate(req *http.Request, errs binding.Errors) binding.Er Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_path"), }) } - f.AuthorizationHeader = "Bearer " + strings.TrimSpace(f.AccessToken) + if !strings.HasPrefix(f.AuthorizationHeader, "Bearer ") { + errs = append(errs, binding.Error{ + FieldNames: []string{"AuthorizationHeader"}, + Classification: "", + Message: ctx.Locale.TrString("form.required_prefix", "Bearer "), + }) + } return errs } diff --git a/stylelint.config.js b/stylelint.config.js deleted file mode 100644 index 523b18841e..0000000000 --- a/stylelint.config.js +++ /dev/null @@ -1,246 +0,0 @@ -import {fileURLToPath} from 'node:url'; - -const cssVarFiles = [ - fileURLToPath(new URL('web_src/css/base.css', import.meta.url)), - fileURLToPath(new URL('web_src/css/themes/theme-gitea-light.css', import.meta.url)), - fileURLToPath(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url)), -]; - -/** @type {import('stylelint').Config} */ -export default { - plugins: [ - 'stylelint-declaration-strict-value', - 'stylelint-declaration-block-no-ignored-properties', - 'stylelint-value-no-unknown-custom-properties', - '@stylistic/stylelint-plugin', - ], - ignoreFiles: [ - '**/*.go', - '/web_src/fomantic', - ], - overrides: [ - { - files: ['**/chroma/*', '**/codemirror/*', '**/standalone/*', '**/console.css', 'font_i18n.css'], - rules: { - 'scale-unlimited/declaration-strict-value': null, - }, - }, - { - files: ['**/chroma/*', '**/codemirror/*'], - rules: { - 'block-no-empty': null, - }, - }, - { - files: ['**/*.vue'], - customSyntax: 'postcss-html', - }, - ], - rules: { - '@stylistic/at-rule-name-case': null, - '@stylistic/at-rule-name-newline-after': null, - '@stylistic/at-rule-name-space-after': null, - '@stylistic/at-rule-semicolon-newline-after': null, - '@stylistic/at-rule-semicolon-space-before': null, - '@stylistic/block-closing-brace-empty-line-before': null, - '@stylistic/block-closing-brace-newline-after': null, - '@stylistic/block-closing-brace-newline-before': null, - '@stylistic/block-closing-brace-space-after': null, - '@stylistic/block-closing-brace-space-before': null, - '@stylistic/block-opening-brace-newline-after': null, - '@stylistic/block-opening-brace-newline-before': null, - '@stylistic/block-opening-brace-space-after': null, - '@stylistic/block-opening-brace-space-before': 'always', - '@stylistic/color-hex-case': 'lower', - '@stylistic/declaration-bang-space-after': 'never', - '@stylistic/declaration-bang-space-before': null, - '@stylistic/declaration-block-semicolon-newline-after': null, - '@stylistic/declaration-block-semicolon-newline-before': null, - '@stylistic/declaration-block-semicolon-space-after': null, - '@stylistic/declaration-block-semicolon-space-before': 'never', - '@stylistic/declaration-block-trailing-semicolon': null, - '@stylistic/declaration-colon-newline-after': null, - '@stylistic/declaration-colon-space-after': null, - '@stylistic/declaration-colon-space-before': 'never', - '@stylistic/function-comma-newline-after': null, - '@stylistic/function-comma-newline-before': null, - '@stylistic/function-comma-space-after': null, - '@stylistic/function-comma-space-before': null, - '@stylistic/function-max-empty-lines': 0, - '@stylistic/function-parentheses-newline-inside': 'never-multi-line', - '@stylistic/function-parentheses-space-inside': null, - '@stylistic/function-whitespace-after': null, - '@stylistic/indentation': 2, - '@stylistic/linebreaks': null, - '@stylistic/max-empty-lines': 1, - '@stylistic/max-line-length': null, - '@stylistic/media-feature-colon-space-after': null, - '@stylistic/media-feature-colon-space-before': 'never', - '@stylistic/media-feature-name-case': null, - '@stylistic/media-feature-parentheses-space-inside': null, - '@stylistic/media-feature-range-operator-space-after': 'always', - '@stylistic/media-feature-range-operator-space-before': 'always', - '@stylistic/media-query-list-comma-newline-after': null, - '@stylistic/media-query-list-comma-newline-before': null, - '@stylistic/media-query-list-comma-space-after': null, - '@stylistic/media-query-list-comma-space-before': null, - '@stylistic/named-grid-areas-alignment': null, - '@stylistic/no-empty-first-line': null, - '@stylistic/no-eol-whitespace': true, - '@stylistic/no-extra-semicolons': true, - '@stylistic/no-missing-end-of-source-newline': null, - '@stylistic/number-leading-zero': null, - '@stylistic/number-no-trailing-zeros': null, - '@stylistic/property-case': 'lower', - '@stylistic/selector-attribute-brackets-space-inside': null, - '@stylistic/selector-attribute-operator-space-after': null, - '@stylistic/selector-attribute-operator-space-before': null, - '@stylistic/selector-combinator-space-after': null, - '@stylistic/selector-combinator-space-before': null, - '@stylistic/selector-descendant-combinator-no-non-space': null, - '@stylistic/selector-list-comma-newline-after': null, - '@stylistic/selector-list-comma-newline-before': null, - '@stylistic/selector-list-comma-space-after': 'always-single-line', - '@stylistic/selector-list-comma-space-before': 'never-single-line', - '@stylistic/selector-max-empty-lines': 0, - '@stylistic/selector-pseudo-class-case': 'lower', - '@stylistic/selector-pseudo-class-parentheses-space-inside': 'never', - '@stylistic/selector-pseudo-element-case': 'lower', - '@stylistic/string-quotes': 'double', - '@stylistic/unicode-bom': null, - '@stylistic/unit-case': 'lower', - '@stylistic/value-list-comma-newline-after': null, - '@stylistic/value-list-comma-newline-before': null, - '@stylistic/value-list-comma-space-after': null, - '@stylistic/value-list-comma-space-before': null, - '@stylistic/value-list-max-empty-lines': 0, - 'alpha-value-notation': null, - 'annotation-no-unknown': true, - 'at-rule-allowed-list': null, - 'at-rule-disallowed-list': null, - 'at-rule-empty-line-before': null, - 'at-rule-no-unknown': [true, {ignoreAtRules: ['tailwind']}], - 'at-rule-no-vendor-prefix': true, - 'at-rule-property-required-list': null, - 'block-no-empty': true, - 'color-function-notation': null, - 'color-hex-alpha': null, - 'color-hex-length': null, - 'color-named': null, - 'color-no-hex': null, - 'color-no-invalid-hex': true, - 'comment-empty-line-before': null, - 'comment-no-empty': true, - 'comment-pattern': null, - 'comment-whitespace-inside': null, - 'comment-word-disallowed-list': null, - 'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}], - 'custom-media-pattern': null, - 'custom-property-empty-line-before': null, - 'custom-property-no-missing-var-function': true, - 'custom-property-pattern': null, - 'declaration-block-no-duplicate-custom-properties': true, - 'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}], - 'declaration-block-no-redundant-longhand-properties': null, - 'declaration-block-no-shorthand-property-overrides': null, - 'declaration-block-single-line-max-declarations': null, - 'declaration-empty-line-before': null, - 'declaration-no-important': null, - 'declaration-property-max-values': null, - 'declaration-property-unit-allowed-list': null, - 'declaration-property-unit-disallowed-list': {'line-height': ['em']}, - 'declaration-property-value-allowed-list': null, - 'declaration-property-value-disallowed-list': null, - 'declaration-property-value-no-unknown': true, - 'font-family-name-quotes': 'always-where-recommended', - 'font-family-no-duplicate-names': true, - 'font-family-no-missing-generic-family-keyword': true, - 'font-weight-notation': null, - 'function-allowed-list': null, - 'function-calc-no-unspaced-operator': true, - 'function-disallowed-list': null, - 'function-linear-gradient-no-nonstandard-direction': true, - 'function-name-case': 'lower', - 'function-no-unknown': true, - 'function-url-no-scheme-relative': null, - 'function-url-quotes': 'always', - 'function-url-scheme-allowed-list': null, - 'function-url-scheme-disallowed-list': null, - 'hue-degree-notation': null, - 'import-notation': 'string', - 'keyframe-block-no-duplicate-selectors': true, - 'keyframe-declaration-no-important': true, - 'keyframe-selector-notation': null, - 'keyframes-name-pattern': null, - 'length-zero-no-unit': [true, {ignore: ['custom-properties']}, {ignoreFunctions: ['var']}], - 'max-nesting-depth': null, - 'media-feature-name-allowed-list': null, - 'media-feature-name-disallowed-list': null, - 'media-feature-name-no-unknown': true, - 'media-feature-name-no-vendor-prefix': true, - 'media-feature-name-unit-allowed-list': null, - 'media-feature-name-value-allowed-list': null, - 'media-feature-name-value-no-unknown': true, - 'media-feature-range-notation': null, - 'media-query-no-invalid': true, - 'named-grid-areas-no-invalid': true, - 'no-descending-specificity': null, - 'no-duplicate-at-import-rules': true, - 'no-duplicate-selectors': true, - 'no-empty-source': true, - 'no-invalid-double-slash-comments': true, - 'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}], - 'no-irregular-whitespace': true, - 'no-unknown-animations': null, - 'no-unknown-custom-properties': null, - 'number-max-precision': null, - 'plugin/declaration-block-no-ignored-properties': true, - 'property-allowed-list': null, - 'property-disallowed-list': null, - 'property-no-unknown': true, - 'property-no-vendor-prefix': null, - 'rule-empty-line-before': null, - 'rule-selector-property-disallowed-list': null, - 'scale-unlimited/declaration-strict-value': [['/color$/', 'font-weight'], {ignoreValues: '/^(inherit|transparent|unset|initial|currentcolor|none)$/', ignoreFunctions: false, disableFix: true, expandShorthand: true}], - 'selector-anb-no-unmatchable': true, - 'selector-attribute-name-disallowed-list': null, - 'selector-attribute-operator-allowed-list': null, - 'selector-attribute-operator-disallowed-list': null, - 'selector-attribute-quotes': 'always', - 'selector-class-pattern': null, - 'selector-combinator-allowed-list': null, - 'selector-combinator-disallowed-list': null, - 'selector-disallowed-list': null, - 'selector-id-pattern': null, - 'selector-max-attribute': null, - 'selector-max-class': null, - 'selector-max-combinators': null, - 'selector-max-compound-selectors': null, - 'selector-max-id': null, - 'selector-max-pseudo-class': null, - 'selector-max-specificity': null, - 'selector-max-type': null, - 'selector-max-universal': null, - 'selector-nested-pattern': null, - 'selector-no-qualifying-type': null, - 'selector-no-vendor-prefix': true, - 'selector-not-notation': null, - 'selector-pseudo-class-allowed-list': null, - 'selector-pseudo-class-disallowed-list': null, - 'selector-pseudo-class-no-unknown': true, - 'selector-pseudo-element-allowed-list': null, - 'selector-pseudo-element-colon-notation': 'double', - 'selector-pseudo-element-disallowed-list': null, - 'selector-pseudo-element-no-unknown': true, - 'selector-type-case': 'lower', - 'selector-type-no-unknown': [true, {ignore: ['custom-elements']}], - 'shorthand-property-no-redundant-values': true, - 'string-no-newline': true, - 'time-min-milliseconds': null, - 'unit-allowed-list': null, - 'unit-disallowed-list': null, - 'unit-no-unknown': true, - 'value-keyword-case': null, - 'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}], - }, -}; diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 8a8bd61148..687a277a84 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -97,10 +97,6 @@ -
    - - -
    diff --git a/templates/admin/auth/source/ldap.tmpl b/templates/admin/auth/source/ldap.tmpl index 6cb6643f26..680849c8ea 100644 --- a/templates/admin/auth/source/ldap.tmpl +++ b/templates/admin/auth/source/ldap.tmpl @@ -71,10 +71,6 @@
    -
    - - -
    diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 9b89b8335f..bfd2ee6670 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -76,8 +76,7 @@ {{ctx.Locale.Tr "admin.dashboard.system_status"}} {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} -
    -
    +
    {{template "admin/system_status" .}}
    diff --git a/templates/admin/layout_head.tmpl b/templates/admin/layout_head.tmpl index 7cc6624d50..8ba47f2f14 100644 --- a/templates/admin/layout_head.tmpl +++ b/templates/admin/layout_head.tmpl @@ -1,6 +1,6 @@ {{template "base/head" .ctxData}}
    -
    +
    {{template "admin/navbar" .ctxData}}
    {{template "base/alert" .ctxData}} diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index 1ec703b296..16ec1b4b5b 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.dashboard"}} - {{if .DatabaseType.IsMySQL}} + {{if or .DatabaseType.IsMySQL .DatabaseType.IsMSSQL}} {{ctx.Locale.Tr "admin.self_check"}} diff --git a/templates/admin/self_check.tmpl b/templates/admin/self_check.tmpl index 5c154ac0d5..94c4673a49 100644 --- a/templates/admin/self_check.tmpl +++ b/templates/admin/self_check.tmpl @@ -8,6 +8,8 @@ {{if .DatabaseCheckHasProblems}} {{if .DatabaseType.IsMySQL}}
    {{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}
    + {{else if .DatabaseType.IsMSSQL}} +
    {{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}
    {{end}} {{if .DatabaseCheckCollationMismatch}}
    {{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}
    diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 5db7464480..75b7845310 100644 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -1,8 +1,6 @@
    {{else}}
    - {{ctx.Locale.Tr "org.teams.repos.none"}} + {{ctx.Locale.Tr "org.teams.repos.none"}}
    {{end}}
    diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index c9f80259e2..9311a46e38 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -22,7 +22,7 @@ {{if .Team.Description}} {{.Team.Description}} {{else}} - {{ctx.Locale.Tr "org.teams.no_desc"}} + {{ctx.Locale.Tr "org.teams.no_desc"}} {{end}}
    {{if eq .Team.LowerName "owners"}} diff --git a/templates/package/shared/cleanup_rules/list.tmpl b/templates/package/shared/cleanup_rules/list.tmpl index ba1683b932..6505c8f9a5 100644 --- a/templates/package/shared/cleanup_rules/list.tmpl +++ b/templates/package/shared/cleanup_rules/list.tmpl @@ -16,26 +16,26 @@ {{.Type.Name}}
    -

    {{if .Enabled}}{{ctx.Locale.Tr "enabled"}}{{else}}{{ctx.Locale.Tr "disabled"}}{{end}}

    + {{if .Enabled}}{{ctx.Locale.Tr "enabled"}}{{else}}{{ctx.Locale.Tr "disabled"}}{{end}}
    {{if .KeepCount}}
    -

    {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:

    {{if eq .KeepCount 1}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}} + {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}: {{if eq .KeepCount 1}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}}
    {{end}} {{if .KeepPattern}}
    -

    {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}:

    {{StringUtils.EllipsisString .KeepPattern 100}} + {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}: {{StringUtils.EllipsisString .KeepPattern 100}}
    {{end}} {{if .RemoveDays}}
    -

    {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:

    {{ctx.Locale.Tr "tool.days" .RemoveDays}} + {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}: {{ctx.Locale.Tr "tool.days" .RemoveDays}}
    {{end}} {{if .RemovePattern}}
    -

    {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}:

    {{StringUtils.EllipsisString .RemovePattern 100}} + {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}: {{StringUtils.EllipsisString .RemovePattern 100}}
    {{end}}
    diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index b892cff996..0908f5648c 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -41,9 +41,9 @@
    {{range .Projects}}
  • -

    +

    {{svg .IconName 16}} - {{.Title}} + {{.Title}}

    diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 3e000660e2..b45174b086 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -1,8 +1,8 @@ {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}} -
    -
    -

    {{.Project.Title}}

    +
    +
    +

    {{.Project.Title}}

    {{if $canWriteProject}}
  • diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index e1881b1d94..81104a70d2 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -98,15 +98,6 @@
    {{ctx.Locale.Tr "repo.release.prerelease_helper"}} - {{if not .DisableDownloadSourceArchives}} -
    -
    - - -
    -
    - {{ctx.Locale.Tr "repo.release.hide_archive_links_helper"}} - {{end}}
    {{if .PageIsEditRelease}} diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl index ed4d5e7eb3..2a4ec577e7 100644 --- a/templates/repo/settings/collaboration.tmpl +++ b/templates/repo/settings/collaboration.tmpl @@ -29,7 +29,7 @@
    - @@ -75,7 +75,7 @@ {{if $allowedToChangeTeams}}
    -
    diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 3410c103a2..da1a321785 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -55,7 +55,7 @@ {{.Fingerprint}}
    -

    {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - {{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}}

    + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - {{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}}
    diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 979df1f5cf..31d5ae77f2 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -135,7 +135,7 @@
    {{.CsrfTokenHtml}} - +
    diff --git a/templates/webhook/shared-settings.tmpl b/templates/repo/settings/webhook/settings.tmpl similarity index 94% rename from templates/webhook/shared-settings.tmpl rename to templates/repo/settings/webhook/settings.tmpl index 80caad7279..0a39643260 100644 --- a/templates/webhook/shared-settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -258,15 +258,14 @@ {{ctx.Locale.Tr "repo.settings.branch_filter_desc"}}
    -{{$skipAuthorizationHeader := or (eq .HookType "sourcehut_builds") (eq .HookType "matrix")}} -{{if not $skipAuthorizationHeader}} - -
    - - + +
    + + + {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}} {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | SafeHTML)}} -
    -{{end}} + {{end}} +
    diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index 7ec9acc84e..fb257bd474 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -68,6 +68,6 @@ {{end}} -{{if and .ReadmeExist (or .IsMarkup .IsPlainText)}} +{{if .ReadmeExist}} {{template "repo/view_file" .}} {{end}} diff --git a/templates/shared/user/org_profile_avatar.tmpl b/templates/shared/user/org_profile_avatar.tmpl index d67f133abf..2ff1e40ca8 100644 --- a/templates/shared/user/org_profile_avatar.tmpl +++ b/templates/shared/user/org_profile_avatar.tmpl @@ -4,7 +4,7 @@
    {{ctx.AvatarUtils.Avatar . 100}} - {{.DisplayName}} + {{.DisplayName}} {{if .Visibility.IsLimited}}
    {{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}
    {{end}} {{if .Visibility.IsPrivate}}
    {{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}
    {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index dbf9eb89e2..bcf370b3fb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1741,232 +1741,6 @@ } } }, - "/orgs/{org}/actions/variables": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "organization" - ], - "summary": "Get an org-level variables list", - "operationId": "getOrgVariablesList", - "parameters": [ - { - "type": "string", - "description": "name of the organization", - "name": "org", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/VariableList" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, - "/orgs/{org}/actions/variables/{variablename}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "organization" - ], - "summary": "Get an org-level variable", - "operationId": "getOrgVariable", - "parameters": [ - { - "type": "string", - "description": "name of the organization", - "name": "org", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/ActionVariable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organization" - ], - "summary": "Update an org-level variable", - "operationId": "updateOrgVariable", - "parameters": [ - { - "type": "string", - "description": "name of the organization", - "name": "org", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/UpdateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when updating an org-level variable" - }, - "204": { - "description": "response when updating an org-level variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "organization" - ], - "summary": "Create an org-level variable", - "operationId": "createOrgVariable", - "parameters": [ - { - "type": "string", - "description": "name of the organization", - "name": "org", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/CreateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when creating an org-level variable" - }, - "204": { - "description": "response when creating an org-level variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "organization" - ], - "summary": "Delete an org-level variable", - "operationId": "deleteOrgVariable", - "parameters": [ - { - "type": "string", - "description": "name of the organization", - "name": "org", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/ActionVariable" - }, - "201": { - "description": "response when deleting a variable" - }, - "204": { - "description": "response when deleting a variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/orgs/{org}/activities/feeds": { "get": { "produces": [ @@ -3711,54 +3485,6 @@ } } }, - "/repos/{owner}/{repo}/actions/secrets": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "List an repo's actions secrets", - "operationId": "repoListActionsSecrets", - "parameters": [ - { - "type": "string", - "description": "owner of the repository", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/SecretList" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/repos/{owner}/{repo}/actions/secrets/{secretname}": { "put": { "consumes": [ @@ -3865,261 +3591,6 @@ } } }, - "/repos/{owner}/{repo}/actions/variables": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Get repo-level variables list", - "operationId": "getRepoVariablesList", - "parameters": [ - { - "type": "string", - "description": "name of the owner", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/VariableList" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, - "/repos/{owner}/{repo}/actions/variables/{variablename}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Get a repo-level variable", - "operationId": "getRepoVariable", - "parameters": [ - { - "type": "string", - "description": "name of the owner", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/ActionVariable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "put": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Update a repo-level variable", - "operationId": "updateRepoVariable", - "parameters": [ - { - "type": "string", - "description": "name of the owner", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/UpdateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when updating a repo-level variable" - }, - "204": { - "description": "response when updating a repo-level variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "post": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Create a repo-level variable", - "operationId": "createRepoVariable", - "parameters": [ - { - "type": "string", - "description": "name of the owner", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/CreateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when creating a repo-level variable" - }, - "204": { - "description": "response when creating a repo-level variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Delete a repo-level variable", - "operationId": "deleteRepoVariable", - "parameters": [ - { - "type": "string", - "description": "name of the owner", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repository", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/ActionVariable" - }, - "201": { - "description": "response when deleting a variable" - }, - "204": { - "description": "response when deleting a variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/repos/{owner}/{repo}/activities/feeds": { "get": { "produces": [ @@ -5258,49 +4729,6 @@ } } }, - "/repos/{owner}/{repo}/compare/{basehead}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "repository" - ], - "summary": "Get commit comparison information", - "operationId": "repoCompareDiff", - "parameters": [ - { - "type": "string", - "description": "owner of the repo", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name of the repo", - "name": "repo", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "compare two branches or commits", - "name": "basehead", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/Compare" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/repos/{owner}/{repo}/contents": { "get": { "produces": [ @@ -15947,194 +15375,6 @@ } } }, - "/user/actions/variables": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Get the user-level list of variables which is created by current doer", - "operationId": "getUserVariablesList", - "parameters": [ - { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results", - "name": "limit", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/VariableList" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, - "/user/actions/variables/{variablename}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Get a user-level variable which is created by current doer", - "operationId": "getUserVariable", - "parameters": [ - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/ActionVariable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Update a user-level variable which is created by current doer", - "operationId": "updateUserVariable", - "parameters": [ - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/UpdateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when updating a variable" - }, - "204": { - "description": "response when updating a variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Create a user-level variable", - "operationId": "createUserVariable", - "parameters": [ - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/CreateVariableOption" - } - } - ], - "responses": { - "201": { - "description": "response when creating a variable" - }, - "204": { - "description": "response when creating a variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - }, - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Delete a user-level variable which is created by current doer", - "operationId": "deleteUserVariable", - "parameters": [ - { - "type": "string", - "description": "name of the variable", - "name": "variablename", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "description": "response when deleting a variable" - }, - "204": { - "description": "response when deleting a variable" - }, - "400": { - "$ref": "#/responses/error" - }, - "404": { - "$ref": "#/responses/notFound" - } - } - } - }, "/user/applications/oauth2": { "get": { "produces": [ @@ -18253,35 +17493,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "ActionVariable": { - "description": "ActionVariable return value of the query API", - "type": "object", - "properties": { - "data": { - "description": "the value of the variable", - "type": "string", - "x-go-name": "Data" - }, - "name": { - "description": "the name of the variable", - "type": "string", - "x-go-name": "Name" - }, - "owner_id": { - "description": "the owner to which the variable belongs", - "type": "integer", - "format": "int64", - "x-go-name": "OwnerID" - }, - "repo_id": { - "description": "the repository to which the variable belongs", - "type": "integer", - "format": "int64", - "x-go-name": "RepoID" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "Activity": { "type": "object", "properties": { @@ -18395,9 +17606,6 @@ "description": "AnnotatedTag represents an annotated tag", "type": "object", "properties": { - "archive_download_count": { - "$ref": "#/definitions/TagArchiveDownloadCount" - }, "message": { "type": "string", "x-go-name": "Message" @@ -19108,25 +18316,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "Compare": { - "type": "object", - "title": "Compare represents a comparison between two commits.", - "properties": { - "commits": { - "type": "array", - "items": { - "$ref": "#/definitions/Commit" - }, - "x-go-name": "Commits" - }, - "total_commits": { - "type": "integer", - "format": "int64", - "x-go-name": "TotalCommits" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "ContentsResponse": { "description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content", "type": "object", @@ -19936,10 +19125,6 @@ "type": "boolean", "x-go-name": "IsDraft" }, - "hide_archive_links": { - "type": "boolean", - "x-go-name": "HideArchiveLinks" - }, "name": { "type": "string", "x-go-name": "Title" @@ -20205,21 +19390,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "CreateVariableOption": { - "description": "CreateVariableOption the option when creating variable", - "type": "object", - "required": [ - "value" - ], - "properties": { - "value": { - "description": "Value of the variable to create", - "type": "string", - "x-go-name": "Value" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "CreateWikiPageOptions": { "description": "CreateWikiPageOptions form for creating wiki", "type": "object", @@ -20845,10 +20015,6 @@ "type": "boolean", "x-go-name": "IsDraft" }, - "hide_archive_links": { - "type": "boolean", - "x-go-name": "HideArchiveLinks" - }, "name": { "type": "string", "x-go-name": "Title" @@ -20953,11 +20119,6 @@ "external_wiki": { "$ref": "#/definitions/ExternalWiki" }, - "globally_editable_wiki": { - "description": "set the globally editable state of the wiki", - "type": "boolean", - "x-go-name": "GloballyEditableWiki" - }, "has_actions": { "description": "either `true` to enable actions unit, or `false` to disable them.", "type": "boolean", @@ -21107,6 +20268,10 @@ "EditUserOption": { "description": "EditUserOption edit user options", "type": "object", + "required": [ + "source_id", + "login_name" + ], "properties": { "active": { "type": "boolean", @@ -23590,9 +22755,6 @@ "description": "Release represents a repository release", "type": "object", "properties": { - "archive_download_count": { - "$ref": "#/definitions/TagArchiveDownloadCount" - }, "assets": { "type": "array", "items": { @@ -23616,10 +22778,6 @@ "type": "boolean", "x-go-name": "IsDraft" }, - "hide_archive_links": { - "type": "boolean", - "x-go-name": "HideArchiveLinks" - }, "html_url": { "type": "string", "x-go-name": "HTMLURL" @@ -23872,10 +23030,6 @@ "type": "string", "x-go-name": "FullName" }, - "globally_editable_wiki": { - "type": "boolean", - "x-go-name": "GloballyEditableWiki" - }, "has_actions": { "type": "boolean", "x-go-name": "HasActions" @@ -24176,9 +23330,6 @@ "description": "Tag represents a repository tag", "type": "object", "properties": { - "archive_download_count": { - "$ref": "#/definitions/TagArchiveDownloadCount" - }, "commit": { "$ref": "#/definitions/CommitMeta" }, @@ -24205,23 +23356,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "TagArchiveDownloadCount": { - "description": "TagArchiveDownloadCount counts how many times a archive was downloaded", - "type": "object", - "properties": { - "tar_gz": { - "type": "integer", - "format": "int64", - "x-go-name": "TarGz" - }, - "zip": { - "type": "integer", - "format": "int64", - "x-go-name": "Zip" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "Team": { "description": "Team represents a team in an organization", "type": "object", @@ -24613,26 +23747,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "UpdateVariableOption": { - "description": "UpdateVariableOption the option when updating variable", - "type": "object", - "required": [ - "value" - ], - "properties": { - "name": { - "description": "New name for the variable. If the field is empty, the variable name won't be updated.", - "type": "string", - "x-go-name": "Name" - }, - "value": { - "description": "Value of the variable to update", - "type": "string", - "x-go-name": "Value" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "User": { "description": "User represents a user", "type": "object", @@ -24730,12 +23844,6 @@ "type": "boolean", "x-go-name": "Restricted" }, - "source_id": { - "description": "The ID of the user's Authentication Source", - "type": "integer", - "format": "int64", - "x-go-name": "SourceID" - }, "starred_repos_count": { "type": "integer", "format": "int64", @@ -25023,12 +24131,6 @@ } } }, - "ActionVariable": { - "description": "ActionVariable", - "schema": { - "$ref": "#/definitions/ActionVariable" - } - }, "ActivityFeedsList": { "description": "ActivityFeedsList", "schema": { @@ -25216,12 +24318,6 @@ } } }, - "Compare": { - "description": "", - "schema": { - "$ref": "#/definitions/Compare" - } - }, "ContentsListResponse": { "description": "ContentsListResponse", "schema": { @@ -25918,15 +25014,6 @@ } } }, - "VariableList": { - "description": "VariableList", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ActionVariable" - } - } - }, "WatchInfo": { "description": "WatchInfo", "schema": { @@ -26002,7 +25089,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/UpdateVariableOption" + "$ref": "#/definitions/CreateOrUpdateSecretOption" } }, "redirect": { diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl index 5dc46dc0a5..d4553ea61b 100644 --- a/templates/user/dashboard/dashboard.tmpl +++ b/templates/user/dashboard/dashboard.tmpl @@ -1,13 +1,15 @@ {{template "base/head" .}}
    {{template "user/dashboard/navbar" .}} -
    -
    - {{template "base/alert" .}} - {{template "user/heatmap" .}} - {{template "user/dashboard/feeds" .}} +
    + {{template "base/alert" .}} +
    +
    + {{template "user/heatmap" .}} + {{template "user/dashboard/feeds" .}} +
    + {{template "user/dashboard/repolist" .}}
    - {{template "user/dashboard/repolist" .}}
    {{template "base/footer" .}} diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index 2781f710ed..34f9b67f8e 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -52,4 +52,4 @@ data.organizationId = {{.ContextUser.ID}}; window.config.pageData.dashboardRepoList = data; -
    +
    diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index 99da119901..bf3b51ee3b 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -1,8 +1,8 @@
    {{$notificationUnreadCount := call .NotificationUnreadCount}} -
    - - {{template "webhook/shared-settings" .}} + {{template "repo/settings/webhook/settings" .}} diff --git a/tests/e2e/README.md b/tests/e2e/README.md index a0a9fc9ec9..e5fd1ca6c0 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -7,6 +7,7 @@ They can be run with make commands for the appropriate backends, namely: make test-sqlite make test-pgsql make test-mysql +make test-mssql ``` Make sure to perform a clean front-end build before running tests: @@ -52,6 +53,16 @@ Start tests based on the database container TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=test TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-e2e-pgsql ``` +## Run mssql e2e tests +Setup a mssql database inside docker +``` +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) +``` +Start tests based on the database container +``` +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-e2e-mssql +``` + ## Running individual tests Example command to run `example.test.e2e.js` test file: @@ -64,10 +75,10 @@ For SQLite: make test-e2e-sqlite#example ``` -For PostgreSQL databases(replace `mysql` to `pgsql`): +For other databases(replace `mssql` to `mysql` or `pgsql`): ``` -TEST_MYSQL_HOST=localhost:1433 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=sa TEST_MYSQL_PASSWORD=MwantsaSecurePassword1 make test-e2e-mysql#example +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-e2e-mssql#example ``` ## Visual testing diff --git a/tests/e2e/debugserver_test.go b/tests/e2e/debugserver_test.go index b1496b22a1..f0f54665e1 100644 --- a/tests/e2e/debugserver_test.go +++ b/tests/e2e/debugserver_test.go @@ -10,7 +10,6 @@ package e2e import ( - "fmt" "net/url" "os" "os/signal" @@ -25,7 +24,7 @@ func TestDebugserver(t *testing.T) { signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) onGiteaRun(t, func(*testing.T, *url.URL) { - fmt.Println(setting.AppURL) + println(setting.AppURL) <-done }) } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 07f0bf52a3..92c90f52e6 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -91,9 +91,6 @@ func TestE2e(t *testing.T) { if _, set := os.LookupEnv("ACCEPT_VISUAL"); set { runArgs = append(runArgs, "--update-snapshots") } - if project := os.Getenv("PLAYWRIGHT_PROJECT"); project != "" { - runArgs = append(runArgs, "--project="+project) - } // Create new test for each input file for _, path := range paths { @@ -106,20 +103,18 @@ func TestE2e(t *testing.T) { cmd := exec.Command(runArgs[0], runArgs...) cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("GITEA_URL=%s", setting.AppURL)) - var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr - err := cmd.Run() if err != nil { // Currently colored output is conflicting. Using Printf until that is resolved. fmt.Printf("%v", stdout.String()) fmt.Printf("%v", stderr.String()) log.Fatal("Playwright Failed: %s", err) + } else { + fmt.Printf("%v", stdout.String()) } - - fmt.Printf("%v", stdout.String()) }) }) } diff --git a/tests/integration/README.md b/tests/integration/README.md index d659d51b20..f6f74ca21f 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -6,6 +6,7 @@ appropriate backends, namely: make test-sqlite make test-pgsql make test-mysql +make test-mssql ``` Make sure to perform a clean build before running tests: @@ -62,6 +63,16 @@ Start tests based on the database container TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=test TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-pgsql ``` +## Run mssql integration tests +Setup a mssql database inside docker +``` +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) +``` +Start tests based on the database container +``` +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql +``` + ## Running individual tests Example command to run GPG test: @@ -72,10 +83,10 @@ For SQLite: make test-sqlite#GPG ``` -For other databases (replace `mysql` to `pgsql`): +For other databases(replace `mssql` to `mysql`, or `pgsql`): ``` -TEST_MYSQL_HOST=localhost:1433 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=sa TEST_MYSQL_PASSWORD=MwantsaSecurePassword1 make test-mysql#GPG +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql#GPG ``` ## Setting timeouts for declaring long-tests and long-flushes diff --git a/tests/integration/README_ZH.md b/tests/integration/README_ZH.md index d0debf2fd6..6aea4ab212 100644 --- a/tests/integration/README_ZH.md +++ b/tests/integration/README_ZH.md @@ -59,6 +59,16 @@ docker run -e "POSTGRES_DB=test" -p 5432:5432 --rm --name pgsql postgres:14 #(ju TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=test TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-pgsql ``` +## Run mssql integration tests +同上,首先在 docker 容器里部署一个 mssql 数据库 +``` +docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm --name mssql microsoft/mssql-server-linux:latest #(just ctrl-c to stop db and clean the container) +``` +之后便可以基于这个数据库进行集成测试 +``` +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql +``` + ## 如何进行自定义的集成测试 下面的示例展示了怎样在集成测试中只进行 GPG 测试: @@ -69,9 +79,9 @@ sqlite 数据库: make test-sqlite#GPG ``` -其它数据库 (用 PGSQL 取代 MYSQL): +其它数据库(把 MSSQL 替换为 MYSQL, PGSQL): ``` -TEST_MYSQL_HOST=localhost:1433 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=sa TEST_MYSQL_PASSWORD=MwantsaSecurePassword1 make test-mysql#GPG +TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql#GPG ``` diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index 0e97e606f9..e8954f5b20 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -196,13 +196,19 @@ func TestAPIEditUser(t *testing.T) { urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2") req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ + // required + "login_name": "user2", + "source_id": "0", + // to change "full_name": "Full Name User 2", }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) empty := "" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - Email: &empty, + LoginName: "user2", + SourceID: 0, + Email: &empty, }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusBadRequest) @@ -214,6 +220,10 @@ func TestAPIEditUser(t *testing.T) { assert.False(t, user2.IsRestricted) bTrue := true req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ + // required + LoginName: "user2", + SourceID: 0, + // to change Restricted: &bTrue, }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) @@ -221,45 +231,6 @@ func TestAPIEditUser(t *testing.T) { assert.True(t, user2.IsRestricted) } -func TestAPIEditUserWithLoginName(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - adminUsername := "user1" - token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) - urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2") - - loginName := "user2" - loginSource := int64(0) - - t.Run("login_name only", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - LoginName: &loginName, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusUnprocessableEntity) - }) - - t.Run("source_id only", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - SourceID: &loginSource, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusUnprocessableEntity) - }) - - t.Run("login_name & source_id", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - LoginName: &loginName, - SourceID: &loginSource, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusOK) - }) -} - func TestAPICreateRepoForUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" @@ -404,14 +375,18 @@ func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) { newEmail := "user2@example1.com" req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - Email: &newEmail, + LoginName: "user2", + SourceID: 0, + Email: &newEmail, }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning")) originalEmail := "user2@example.com" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - Email: &originalEmail, + LoginName: "user2", + SourceID: 0, + Email: &originalEmail, }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) } diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go index abb9852eef..528890ca22 100644 --- a/tests/integration/api_notification_test.go +++ b/tests/integration/api_notification_test.go @@ -111,7 +111,7 @@ func TestAPINotification(t *testing.T) { MakeRequest(t, NewRequest(t, "GET", "/api/v1/notifications/new"), http.StatusUnauthorized) - newStruct := struct { + new := struct { New int64 `json:"new"` }{} @@ -119,8 +119,8 @@ func TestAPINotification(t *testing.T) { req = NewRequest(t, "GET", "/api/v1/notifications/new"). AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &newStruct) - assert.True(t, newStruct.New > 0) + DecodeJSON(t, resp, &new) + assert.True(t, new.New > 0) // -- mark notifications as read -- req = NewRequest(t, "GET", "/api/v1/notifications?status-types=unread"). @@ -153,8 +153,8 @@ func TestAPINotification(t *testing.T) { req = NewRequest(t, "GET", "/api/v1/notifications/new"). AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &newStruct) - assert.True(t, newStruct.New == 0) + DecodeJSON(t, resp, &new) + assert.True(t, new.New == 0) } func TestAPINotificationPUT(t *testing.T) { diff --git a/tests/integration/api_packages_alpine_test.go b/tests/integration/api_packages_alpine_test.go index f70d3c23af..ea9735236b 100644 --- a/tests/integration/api_packages_alpine_test.go +++ b/tests/integration/api_packages_alpine_test.go @@ -480,6 +480,7 @@ aiIK5QoSDwAAAAAAAAAAAAAAAP/IK49O1e8AKAAA` req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/x86_64/%s-%s.apk", rootURL, branch, repository, pkg, packageVersion)). AddBasicAuth(user.Name) MakeRequest(t, req, http.StatusNoContent) + } // Deleting the last file of an architecture should remove that index req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository)) diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 0a392d0a15..49aa4c4e1b 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -133,18 +133,14 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { assert.Equal(t, newRelease.TagName, release.TagName) assert.Equal(t, newRelease.Title, release.Title) assert.Equal(t, newRelease.Note, release.Note) - assert.False(t, newRelease.HideArchiveLinks) - - hideArchiveLinks := true req = NewRequestWithJSON(t, "PATCH", urlStr, &api.EditReleaseOption{ - TagName: release.TagName, - Title: release.Title, - Note: "updated", - IsDraft: &release.IsDraft, - IsPrerelease: &release.IsPrerelease, - Target: release.Target, - HideArchiveLinks: &hideArchiveLinks, + TagName: release.TagName, + Title: release.Title, + Note: "updated", + IsDraft: &release.IsDraft, + IsPrerelease: &release.IsPrerelease, + Target: release.Target, }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) @@ -156,7 +152,6 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { } unittest.AssertExistsAndLoadBean(t, rel) assert.EqualValues(t, rel.Note, newRelease.Note) - assert.True(t, newRelease.HideArchiveLinks) } func TestAPICreateReleaseToDefaultBranch(t *testing.T) { @@ -324,39 +319,3 @@ func TestAPIUploadAssetRelease(t *testing.T) { assert.EqualValues(t, 104, attachment.Size) }) } - -func TestAPIGetReleaseArchiveDownloadCount(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - name := "ReleaseDownloadCount" - - createNewReleaseUsingAPI(t, session, token, owner, repo, name, "", name, "test") - - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, name) - - req := NewRequest(t, "GET", urlStr) - resp := MakeRequest(t, req, http.StatusOK) - - var release *api.Release - DecodeJSON(t, resp, &release) - - // Check if everything defaults to 0 - assert.Equal(t, int64(0), release.ArchiveDownloadCount.TarGz) - assert.Equal(t, int64(0), release.ArchiveDownloadCount.Zip) - - // Download the tarball to increase the count - MakeRequest(t, NewRequest(t, "GET", release.TarURL), http.StatusOK) - - // Check if the count has increased - resp = MakeRequest(t, req, http.StatusOK) - - DecodeJSON(t, resp, &release) - - assert.Equal(t, int64(1), release.ArchiveDownloadCount.TarGz) - assert.Equal(t, int64(0), release.ArchiveDownloadCount.Zip) -} diff --git a/tests/integration/api_repo_archive_test.go b/tests/integration/api_repo_archive_test.go index a4ae599615..c574d49450 100644 --- a/tests/integration/api_repo_archive_test.go +++ b/tests/integration/api_repo_archive_test.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "net/url" - "regexp" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -42,16 +41,6 @@ func TestAPIDownloadArchive(t *testing.T) { assert.Len(t, bs, 266) assert.EqualValues(t, "application/gzip", resp.Header().Get("Content-Type")) - // Must return a link to a commit ID as the "immutable" archive link - linkHeaderRe := regexp.MustCompile(`<(?Phttps?://.*/api/v1/repos/user2/repo1/archive/[a-f0-9]+\.tar\.gz.*)>; rel="immutable"`) - m := linkHeaderRe.FindStringSubmatch(resp.Header().Get("Link")) - assert.NotEmpty(t, m[1]) - resp = MakeRequest(t, NewRequest(t, "GET", m[1]).AddTokenAuth(token), http.StatusOK) - bs2, err := io.ReadAll(resp.Body) - assert.NoError(t, err) - // The locked URL should give the same bytes as the non-locked one - assert.EqualValues(t, bs, bs2) - link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.bundle", user2.Name, repo.Name)) resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) bs, err = io.ReadAll(resp.Body) diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go deleted file mode 100644 index f3188eb49f..0000000000 --- a/tests/integration/api_repo_compare_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "net/http" - "testing" - - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/tests" - - "github.com/stretchr/testify/assert" -) - -func TestAPICompareBranches(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Login as User2. - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - repoName := "repo20" - - req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - - var apiResp *api.Compare - DecodeJSON(t, resp, &apiResp) - - assert.Equal(t, 2, apiResp.TotalCommits) - assert.Len(t, apiResp.Commits, 2) -} diff --git a/tests/integration/api_repo_secrets_test.go b/tests/integration/api_repo_secrets_test.go index c3074d9ece..feb9bae2b2 100644 --- a/tests/integration/api_repo_secrets_test.go +++ b/tests/integration/api_repo_secrets_test.go @@ -24,12 +24,6 @@ func TestAPIRepoSecrets(t *testing.T) { session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - t.Run("List", func(t *testing.T) { - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/secrets", repo.FullName())). - AddTokenAuth(token) - MakeRequest(t, req, http.StatusOK) - }) - t.Run("Create", func(t *testing.T) { cases := []struct { Name string @@ -37,7 +31,7 @@ func TestAPIRepoSecrets(t *testing.T) { }{ { Name: "", - ExpectedStatus: http.StatusMethodNotAllowed, + ExpectedStatus: http.StatusNotFound, }, { Name: "-", diff --git a/tests/integration/api_repo_tags_test.go b/tests/integration/api_repo_tags_test.go index 10a82e11a8..c6eeb404c0 100644 --- a/tests/integration/api_repo_tags_test.go +++ b/tests/integration/api_repo_tags_test.go @@ -85,39 +85,3 @@ func createNewTagUsingAPI(t *testing.T, session *TestSession, token, ownerName, DecodeJSON(t, resp, &respObj) return &respObj } - -func TestAPIGetTagArchiveDownloadCount(t *testing.T) { - defer tests.PrepareTestEnv(t)() - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Login as User2. - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - repoName := "repo1" - tagName := "TagDownloadCount" - - createNewTagUsingAPI(t, session, token, user.Name, repoName, tagName, "", "") - - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/%s?token=%s", user.Name, repoName, tagName, token) - - req := NewRequest(t, "GET", urlStr) - resp := MakeRequest(t, req, http.StatusOK) - - var tagInfo *api.Tag - DecodeJSON(t, resp, &tagInfo) - - // Check if everything defaults to 0 - assert.Equal(t, int64(0), tagInfo.ArchiveDownloadCount.TarGz) - assert.Equal(t, int64(0), tagInfo.ArchiveDownloadCount.Zip) - - // Download the tarball to increase the count - MakeRequest(t, NewRequest(t, "GET", tagInfo.TarballURL), http.StatusOK) - - // Check if the count has increased - resp = MakeRequest(t, req, http.StatusOK) - - DecodeJSON(t, resp, &tagInfo) - - assert.Equal(t, int64(1), tagInfo.ArchiveDownloadCount.TarGz) - assert.Equal(t, int64(0), tagInfo.ArchiveDownloadCount.Zip) -} diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 2fb89cfa6e..96f38edc86 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -684,7 +684,9 @@ func TestAPIRepoGetReviewers(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) var reviewers []*api.User DecodeJSON(t, resp, &reviewers) - assert.Len(t, reviewers, 4) + if assert.Len(t, reviewers, 3) { + assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID}) + } } func TestAPIRepoGetAssignees(t *testing.T) { diff --git a/tests/integration/api_repo_variables_test.go b/tests/integration/api_repo_variables_test.go deleted file mode 100644 index 7847962b07..0000000000 --- a/tests/integration/api_repo_variables_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "testing" - - auth_model "code.gitea.io/gitea/models/auth" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/tests" -) - -func TestAPIRepoVariables(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - t.Run("CreateRepoVariable", func(t *testing.T) { - cases := []struct { - Name string - ExpectedStatus int - }{ - { - Name: "-", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "_", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: "TEST_VAR", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: "test_var", - ExpectedStatus: http.StatusConflict, - }, - { - Name: "ci", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "123var", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "var@test", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "github_var", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "gitea_var", - ExpectedStatus: http.StatusBadRequest, - }, - } - - for _, c := range cases { - req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/variables/%s", repo.FullName(), c.Name), api.CreateVariableOption{ - Value: "value", - }).AddTokenAuth(token) - MakeRequest(t, req, c.ExpectedStatus) - } - }) - - t.Run("UpdateRepoVariable", func(t *testing.T) { - variableName := "test_update_var" - url := fmt.Sprintf("/api/v1/repos/%s/actions/variables/%s", repo.FullName(), variableName) - req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{ - Value: "initial_val", - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - cases := []struct { - Name string - UpdateName string - ExpectedStatus int - }{ - { - Name: "not_found_var", - ExpectedStatus: http.StatusNotFound, - }, - { - Name: variableName, - UpdateName: "1invalid", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "invalid@name", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "ci", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "updated_var_name", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: variableName, - ExpectedStatus: http.StatusNotFound, - }, - { - Name: "updated_var_name", - ExpectedStatus: http.StatusNoContent, - }, - } - - for _, c := range cases { - req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/actions/variables/%s", repo.FullName(), c.Name), api.UpdateVariableOption{ - Name: c.UpdateName, - Value: "updated_val", - }).AddTokenAuth(token) - MakeRequest(t, req, c.ExpectedStatus) - } - }) - - t.Run("DeleteRepoVariable", func(t *testing.T) { - variableName := "test_delete_var" - url := fmt.Sprintf("/api/v1/repos/%s/actions/variables/%s", repo.FullName(), variableName) - - req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{ - Value: "initial_val", - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - req = NewRequest(t, "DELETE", url).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - req = NewRequest(t, "DELETE", url).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNotFound) - }) -} diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index 0e01b504cc..f776b35325 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -10,9 +10,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -59,25 +57,6 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) { } } -func TestAPIUserSearchPaged(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.API.DefaultPagingNum, 5)() - - req := NewRequest(t, "GET", "/api/v1/users/search?limit=1") - resp := MakeRequest(t, req, http.StatusOK) - - var limitedResults SearchResults - DecodeJSON(t, resp, &limitedResults) - assert.Len(t, limitedResults.Data, 1) - - req = NewRequest(t, "GET", "/api/v1/users/search") - resp = MakeRequest(t, req, http.StatusOK) - - var results SearchResults - DecodeJSON(t, resp, &results) - assert.Len(t, results.Data, 5) -} - func TestAPIUserSearchSystemUsers(t *testing.T) { defer tests.PrepareTestEnv(t)() for _, systemUser := range []*user_model.User{ diff --git a/tests/integration/api_user_variables_test.go b/tests/integration/api_user_variables_test.go deleted file mode 100644 index dd5501f0b9..0000000000 --- a/tests/integration/api_user_variables_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "testing" - - auth_model "code.gitea.io/gitea/models/auth" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/tests" -) - -func TestAPIUserVariables(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) - - t.Run("CreateRepoVariable", func(t *testing.T) { - cases := []struct { - Name string - ExpectedStatus int - }{ - { - Name: "-", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "_", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: "TEST_VAR", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: "test_var", - ExpectedStatus: http.StatusConflict, - }, - { - Name: "ci", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "123var", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "var@test", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "github_var", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: "gitea_var", - ExpectedStatus: http.StatusBadRequest, - }, - } - - for _, c := range cases { - req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/actions/variables/%s", c.Name), api.CreateVariableOption{ - Value: "value", - }).AddTokenAuth(token) - MakeRequest(t, req, c.ExpectedStatus) - } - }) - - t.Run("UpdateRepoVariable", func(t *testing.T) { - variableName := "test_update_var" - url := fmt.Sprintf("/api/v1/user/actions/variables/%s", variableName) - req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{ - Value: "initial_val", - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - cases := []struct { - Name string - UpdateName string - ExpectedStatus int - }{ - { - Name: "not_found_var", - ExpectedStatus: http.StatusNotFound, - }, - { - Name: variableName, - UpdateName: "1invalid", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "invalid@name", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "ci", - ExpectedStatus: http.StatusBadRequest, - }, - { - Name: variableName, - UpdateName: "updated_var_name", - ExpectedStatus: http.StatusNoContent, - }, - { - Name: variableName, - ExpectedStatus: http.StatusNotFound, - }, - { - Name: "updated_var_name", - ExpectedStatus: http.StatusNoContent, - }, - } - - for _, c := range cases { - req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/user/actions/variables/%s", c.Name), api.UpdateVariableOption{ - Name: c.UpdateName, - Value: "updated_val", - }).AddTokenAuth(token) - MakeRequest(t, req, c.ExpectedStatus) - } - }) - - t.Run("DeleteRepoVariable", func(t *testing.T) { - variableName := "test_delete_var" - url := fmt.Sprintf("/api/v1/user/actions/variables/%s", variableName) - - req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{ - Value: "initial_val", - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - req = NewRequest(t, "DELETE", url).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNoContent) - - req = NewRequest(t, "DELETE", url).AddTokenAuth(token) - MakeRequest(t, req, http.StatusNotFound) - }) -} diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index 400cf068b4..c61b4a061b 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -15,8 +15,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -288,63 +286,6 @@ func TestAPIEditOtherWikiPage(t *testing.T) { testCreateWiki(http.StatusCreated) } -func TestAPISetWikiGlobalEditability(t *testing.T) { - defer tests.PrepareTestEnv(t)() - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - // Create a new repository for testing purposes - repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{ - unit_model.TypeCode, - unit_model.TypeWiki, - }, nil, nil) - defer f() - urlStr := fmt.Sprintf("/api/v1/repos/%s", repo.FullName()) - - assertGlobalEditability := func(t *testing.T, editability bool) { - t.Helper() - - req := NewRequest(t, "GET", urlStr) - resp := MakeRequest(t, req, http.StatusOK) - - var opts api.Repository - DecodeJSON(t, resp, &opts) - - assert.Equal(t, opts.GloballyEditableWiki, editability) - } - - t.Run("api includes GloballyEditableWiki", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - assertGlobalEditability(t, false) - }) - - t.Run("api can turn on GloballyEditableWiki", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - globallyEditable := true - req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ - GloballyEditableWiki: &globallyEditable, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusOK) - - assertGlobalEditability(t, true) - }) - - t.Run("disabling the wiki disables GloballyEditableWiki", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - hasWiki := false - req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ - HasWiki: &hasWiki, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusOK) - - assertGlobalEditability(t, false) - }) -} - func TestAPIListPageRevisions(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" @@ -383,28 +324,3 @@ func TestAPIListPageRevisions(t *testing.T) { assert.Equal(t, dummyrevisions, revisions) } - -func TestAPIWikiNonMasterBranch(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - repo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{ - WikiBranch: optional.Some("main"), - }) - defer f() - - uris := []string{ - "revisions/Home", - "pages", - "page/Home", - } - baseURL := fmt.Sprintf("/api/v1/repos/%s/wiki", repo.FullName()) - for _, uri := range uris { - t.Run(uri, func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequestf(t, "GET", "%s/%s", baseURL, uri) - MakeRequest(t, req, http.StatusOK) - }) - } -} diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go index 06677287c0..3a5fdb97a6 100644 --- a/tests/integration/auth_ldap_test.go +++ b/tests/integration/auth_ldap_test.go @@ -112,17 +112,13 @@ func getLDAPServerPort() string { return port } -func buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, mailKeyAttribute, defaultDomainName, groupFilter, groupTeamMap, groupTeamMapRemoval string) map[string]string { +func buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, groupFilter, groupTeamMap, groupTeamMapRemoval string) map[string]string { // Modify user filter to test group filter explicitly userFilter := "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))" if groupFilter != "" { userFilter = "(&(objectClass=inetOrgPerson)(uid=%s))" } - if len(mailKeyAttribute) == 0 { - mailKeyAttribute = "mail" - } - return map[string]string{ "_csrf": csrf, "type": "2", @@ -138,9 +134,8 @@ func buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, mailKeyAttribute, default "attribute_username": "uid", "attribute_name": "givenName", "attribute_surname": "sn", - "attribute_mail": mailKeyAttribute, + "attribute_mail": "mail", "attribute_ssh_public_key": sshKeyAttribute, - "default_domain_name": defaultDomainName, "is_sync_enabled": "on", "is_active": "on", "groups_enabled": "on", @@ -153,7 +148,7 @@ func buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, mailKeyAttribute, default } } -func addAuthSourceLDAP(t *testing.T, sshKeyAttribute, mailKeyAttribute, defaultDomainName, groupFilter string, groupMapParams ...string) { +func addAuthSourceLDAP(t *testing.T, sshKeyAttribute, groupFilter string, groupMapParams ...string) { groupTeamMapRemoval := "off" groupTeamMap := "" if len(groupMapParams) == 2 { @@ -162,7 +157,7 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute, mailKeyAttribute, defaultD } session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") - req := NewRequestWithValues(t, "POST", "/admin/auths/new", buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, mailKeyAttribute, defaultDomainName, groupFilter, groupTeamMap, groupTeamMapRemoval)) + req := NewRequestWithValues(t, "POST", "/admin/auths/new", buildAuthSourceLDAPPayload(csrf, sshKeyAttribute, groupFilter, groupTeamMap, groupTeamMapRemoval)) session.MakeRequest(t, req, http.StatusSeeOther) } @@ -172,7 +167,7 @@ func TestLDAPUserSignin(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "") + addAuthSourceLDAP(t, "", "") u := gitLDAPUsers[0] @@ -189,7 +184,7 @@ func TestLDAPUserSignin(t *testing.T) { func TestLDAPAuthChange(t *testing.T) { defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "") + addAuthSourceLDAP(t, "", "") session := loginUser(t, "user1") req := NewRequest(t, "GET", "/admin/auths") @@ -210,7 +205,7 @@ func TestLDAPAuthChange(t *testing.T) { binddn, _ := doc.Find(`input[name="bind_dn"]`).Attr("value") assert.Equal(t, "uid=gitea,ou=service,dc=planetexpress,dc=com", binddn) - req = NewRequestWithValues(t, "POST", href, buildAuthSourceLDAPPayload(csrf, "", "", "", "", "", "off")) + req = NewRequestWithValues(t, "POST", href, buildAuthSourceLDAPPayload(csrf, "", "", "", "off")) session.MakeRequest(t, req, http.StatusSeeOther) req = NewRequest(t, "GET", href) @@ -220,21 +215,6 @@ func TestLDAPAuthChange(t *testing.T) { assert.Equal(t, host, getLDAPServerHost()) binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value") assert.Equal(t, "uid=gitea,ou=service,dc=planetexpress,dc=com", binddn) - domainname, _ := doc.Find(`input[name="default_domain_name"]`).Attr("value") - assert.Equal(t, "", domainname) - - req = NewRequestWithValues(t, "POST", href, buildAuthSourceLDAPPayload(csrf, "", "", "test.org", "", "", "off")) - session.MakeRequest(t, req, http.StatusSeeOther) - - req = NewRequest(t, "GET", href) - resp = session.MakeRequest(t, req, http.StatusOK) - doc = NewHTMLParser(t, resp.Body) - host, _ = doc.Find(`input[name="host"]`).Attr("value") - assert.Equal(t, host, getLDAPServerHost()) - binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value") - assert.Equal(t, "uid=gitea,ou=service,dc=planetexpress,dc=com", binddn) - domainname, _ = doc.Find(`input[name="default_domain_name"]`).Attr("value") - assert.Equal(t, "test.org", domainname) } func TestLDAPUserSync(t *testing.T) { @@ -243,7 +223,7 @@ func TestLDAPUserSync(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "") + addAuthSourceLDAP(t, "", "") auth.SyncExternalUsers(context.Background(), true) // Check if users exists @@ -272,7 +252,7 @@ func TestLDAPUserSyncWithEmptyUsernameAttribute(t *testing.T) { session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") - payload := buildAuthSourceLDAPPayload(csrf, "", "", "", "", "", "") + payload := buildAuthSourceLDAPPayload(csrf, "", "", "", "") payload["attribute_username"] = "" req := NewRequestWithValues(t, "POST", "/admin/auths/new", payload) session.MakeRequest(t, req, http.StatusSeeOther) @@ -320,7 +300,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "(cn=git)") + addAuthSourceLDAP(t, "", "(cn=git)") // Assert a user not a member of the LDAP group "cn=git" cannot login // This test may look like TestLDAPUserSigninFailed but it is not. @@ -379,7 +359,7 @@ func TestLDAPUserSigninFailed(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "") + addAuthSourceLDAP(t, "", "") u := otherLDAPUsers[0] testLoginFailed(t, u.UserName, u.Password, translation.NewLocale("en-US").TrString("form.username_password_incorrect")) @@ -391,7 +371,7 @@ func TestLDAPUserSSHKeySync(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "sshPublicKey", "", "", "") + addAuthSourceLDAP(t, "sshPublicKey", "") auth.SyncExternalUsers(context.Background(), true) @@ -424,7 +404,7 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "", "on", `{"cn=ship_crew,ou=people,dc=planetexpress,dc=com":{"org26": ["team11"]},"cn=admin_staff,ou=people,dc=planetexpress,dc=com": {"non-existent": ["non-existent"]}}`) + addAuthSourceLDAP(t, "", "", "on", `{"cn=ship_crew,ou=people,dc=planetexpress,dc=com":{"org26": ["team11"]},"cn=admin_staff,ou=people,dc=planetexpress,dc=com": {"non-existent": ["non-existent"]}}`) org, err := organization.GetOrgByName(db.DefaultContext, "org26") assert.NoError(t, err) team, err := organization.GetTeam(db.DefaultContext, org.ID, "team11") @@ -469,7 +449,7 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { return } defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "", "", "", "on", `{"cn=dispatch,ou=people,dc=planetexpress,dc=com": {"org26": ["team11"]}}`) + addAuthSourceLDAP(t, "", "", "on", `{"cn=dispatch,ou=people,dc=planetexpress,dc=com": {"org26": ["team11"]}}`) org, err := organization.GetOrgByName(db.DefaultContext, "org26") assert.NoError(t, err) team, err := organization.GetTeam(db.DefaultContext, org.ID, "team11") @@ -507,58 +487,6 @@ func TestLDAPPreventInvalidGroupTeamMap(t *testing.T) { session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") - req := NewRequestWithValues(t, "POST", "/admin/auths/new", buildAuthSourceLDAPPayload(csrf, "", "", "", "", `{"NOT_A_VALID_JSON"["MISSING_DOUBLE_POINT"]}`, "off")) + req := NewRequestWithValues(t, "POST", "/admin/auths/new", buildAuthSourceLDAPPayload(csrf, "", "", `{"NOT_A_VALID_JSON"["MISSING_DOUBLE_POINT"]}`, "off")) session.MakeRequest(t, req, http.StatusOK) // StatusOK = failed, StatusSeeOther = ok } - -func TestLDAPUserSyncInvalidMail(t *testing.T) { - if skipLDAPTests() { - t.Skip() - return - } - defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "nonexisting", "", "") - auth.SyncExternalUsers(context.Background(), true) - - // Check if users exists - for _, gitLDAPUser := range gitLDAPUsers { - dbUser, err := user_model.GetUserByName(db.DefaultContext, gitLDAPUser.UserName) - assert.NoError(t, err) - assert.Equal(t, gitLDAPUser.UserName, dbUser.Name) - assert.Equal(t, gitLDAPUser.UserName+"@localhost.local", dbUser.Email) - assert.Equal(t, gitLDAPUser.IsAdmin, dbUser.IsAdmin) - assert.Equal(t, gitLDAPUser.IsRestricted, dbUser.IsRestricted) - } - - // Check if no users exist - for _, otherLDAPUser := range otherLDAPUsers { - _, err := user_model.GetUserByName(db.DefaultContext, otherLDAPUser.UserName) - assert.True(t, user_model.IsErrUserNotExist(err)) - } -} - -func TestLDAPUserSyncInvalidMailDefaultDomain(t *testing.T) { - if skipLDAPTests() { - t.Skip() - return - } - defer tests.PrepareTestEnv(t)() - addAuthSourceLDAP(t, "", "nonexisting", "test.org", "") - auth.SyncExternalUsers(context.Background(), true) - - // Check if users exists - for _, gitLDAPUser := range gitLDAPUsers { - dbUser, err := user_model.GetUserByName(db.DefaultContext, gitLDAPUser.UserName) - assert.NoError(t, err) - assert.Equal(t, gitLDAPUser.UserName, dbUser.Name) - assert.Equal(t, gitLDAPUser.UserName+"@test.org", dbUser.Email) - assert.Equal(t, gitLDAPUser.IsAdmin, dbUser.IsAdmin) - assert.Equal(t, gitLDAPUser.IsRestricted, dbUser.IsRestricted) - } - - // Check if no users exist - for _, otherLDAPUser := range otherLDAPUsers { - _, err := user_model.GetUserByName(db.DefaultContext, otherLDAPUser.UserName) - assert.True(t, user_model.IsErrUserNotExist(err)) - } -} diff --git a/tests/integration/benchmarks_test.go b/tests/integration/benchmarks_test.go index 62da761d2d..7a882fe836 100644 --- a/tests/integration/benchmarks_test.go +++ b/tests/integration/benchmarks_test.go @@ -4,7 +4,7 @@ package integration import ( - "math/rand/v2" + "math/rand" "net/http" "net/url" "testing" @@ -18,7 +18,7 @@ import ( func StringWithCharset(length int, charset string) string { b := make([]byte, length) for i := range b { - b[i] = charset[rand.IntN(len(charset))] + b[i] = charset[rand.Intn(len(charset))] } return string(b) } @@ -37,7 +37,7 @@ func BenchmarkRepoBranchCommit(b *testing.B) { b.ResetTimer() b.Run("CreateBranch", func(b *testing.B) { b.StopTimer() - branchName := StringWithCharset(5+rand.IntN(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + branchName := StringWithCharset(5+rand.Intn(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") b.StartTimer() for i := 0; i < b.N; i++ { b.Run("new_"+branchName, func(b *testing.B) { diff --git a/tests/integration/db_collation_test.go b/tests/integration/db_collation_test.go index 4d822c45a6..eee26d1ed1 100644 --- a/tests/integration/db_collation_test.go +++ b/tests/integration/db_collation_test.go @@ -39,7 +39,7 @@ func TestDatabaseCollationSelfCheckUI(t *testing.T) { htmlDoc.AssertElement(t, "a.item[href*='/admin/self_check']", exists) } - if setting.Database.Type.IsMySQL() { + if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() { assertSelfCheckExists(true) } else { assertSelfCheckExists(false) @@ -61,9 +61,10 @@ func TestDatabaseCollation(t *testing.T) { assert.EqualValues(t, 2, cnt) _, _ = x.Exec("DROP TABLE IF EXISTS test_collation_tbl") - // by default, SQLite3 and PostgreSQL are using case-sensitive collations, but MySQL is not. - if !setting.Database.Type.IsMySQL() { - t.Skip("only MySQL requires the case-sensitive collation check at the moment") + // by default, SQLite3 and PostgreSQL are using case-sensitive collations, but MySQL and MSSQL are not + // the following tests are only for MySQL and MSSQL + if !setting.Database.Type.IsMySQL() && !setting.Database.Type.IsMSSQL() { + t.Skip("only MySQL and MSSQL requires the case-sensitive collation check at the moment") return } @@ -85,11 +86,20 @@ func TestDatabaseCollation(t *testing.T) { assert.True(t, r.CollationEquals("abc", "abc")) assert.True(t, r.CollationEquals("abc", "utf8mb4_abc")) assert.False(t, r.CollationEquals("utf8mb4_general_ci", "utf8mb4_unicode_ci")) + } else if setting.Database.Type.IsMSSQL() { + assert.True(t, r.IsCollationCaseSensitive("Latin1_General_CS_AS")) + assert.False(t, r.IsCollationCaseSensitive("Latin1_General_CI_AS")) + assert.True(t, r.CollationEquals("abc", "abc")) + assert.False(t, r.CollationEquals("Latin1_General_CS_AS", "SQL_Latin1_General_CP1_CS_AS")) } else { assert.Fail(t, "unexpected database type") } }) + if setting.Database.Type.IsMSSQL() { + return // skip table converting tests because MSSQL doesn't have a simple solution at the moment + } + t.Run("Convert tables to utf8mb4_bin", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index ae0aea237b..3e8416925b 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -404,7 +404,7 @@ func TestCommitMail(t *testing.T) { t.Run("Upload", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - // Upload two separate times, so we have two different 'uploads' that can + // Upload two seperate times, so we have two different 'uploads' that can // be used indepently of each other. uploadFile := func(t *testing.T, name, content string) string { t.Helper() diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 6ee3be2df2..f833366568 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -5,9 +5,9 @@ package integration import ( "bytes" - "crypto/rand" "encoding/hex" "fmt" + "math/rand" "net/http" "net/url" "os" diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_git_test.go index 3ba4a5882c..00890cfb38 100644 --- a/tests/integration/gpg_git_test.go +++ b/tests/integration/gpg_git_test.go @@ -19,9 +19,9 @@ import ( "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" - "github.com/ProtonMail/go-crypto/openpgp" - "github.com/ProtonMail/go-crypto/openpgp/armor" "github.com/stretchr/testify/assert" + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/armor" ) func TestGPGGit(t *testing.T) { diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index f6daf0d146..f1c6b9f135 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -42,7 +42,6 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/services/auth/source/remote" gitea_context "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" @@ -54,8 +53,7 @@ import ( gouuid "github.com/google/uuid" "github.com/markbates/goth" "github.com/markbates/goth/gothic" - goth_gitlab "github.com/markbates/goth/providers/github" - goth_github "github.com/markbates/goth/providers/gitlab" + goth_gitlab "github.com/markbates/goth/providers/gitlab" "github.com/santhosh-tekuri/jsonschema/v5" "github.com/stretchr/testify/assert" ) @@ -340,36 +338,6 @@ func authSourcePayloadGitLabCustom(name string) map[string]string { return payload } -func authSourcePayloadGitHub(name string) map[string]string { - payload := authSourcePayloadOAuth2(name) - payload["oauth2_provider"] = "github" - return payload -} - -func authSourcePayloadGitHubCustom(name string) map[string]string { - payload := authSourcePayloadGitHub(name) - payload["oauth2_use_custom_url"] = "on" - payload["oauth2_auth_url"] = goth_github.AuthURL - payload["oauth2_token_url"] = goth_github.TokenURL - payload["oauth2_profile_url"] = goth_github.ProfileURL - return payload -} - -func createRemoteAuthSource(t *testing.T, name, url, matchingSource string) *auth.Source { - assert.NoError(t, auth.CreateSource(context.Background(), &auth.Source{ - Type: auth.Remote, - Name: name, - IsActive: true, - Cfg: &remote.Source{ - URL: url, - MatchingSource: matchingSource, - }, - })) - source, err := auth.GetSourceByName(context.Background(), name) - assert.NoError(t, err) - return source -} - func createUser(ctx context.Context, t testing.TB, user *user_model.User) func() { user.MustChangePassword = false user.LowerName = strings.ToLower(user.Name) @@ -655,7 +623,7 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile schema, err := jsonschema.Compile(schemaFilePath) assert.NoError(t, err) - var data any + var data interface{} err = json.Unmarshal(resp.Body.Bytes(), &data) assert.NoError(t, err) diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index b63127be82..94ba670b71 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -15,7 +15,6 @@ import ( "testing" "time" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -195,93 +194,6 @@ func TestNewIssue(t *testing.T) { testNewIssue(t, session, "user2", "repo1", "Title", "Description") } -func TestIssueDependencies(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) - - repo, _, f := CreateDeclarativeRepoWithOptions(t, owner, DeclarativeRepoOptions{}) - defer f() - - createIssue := func(t *testing.T, title string) api.Issue { - t.Helper() - - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name) - req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ - Body: "", - Title: title, - }).AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusCreated) - - var apiIssue api.Issue - DecodeJSON(t, resp, &apiIssue) - - return apiIssue - } - addDependency := func(t *testing.T, issue, dependency api.Issue) { - t.Helper() - - urlStr := fmt.Sprintf("/%s/%s/issues/%d/dependency/add", owner.Name, repo.Name, issue.Index) - req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ - "_csrf": GetCSRF(t, session, fmt.Sprintf("/%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)), - "newDependency": fmt.Sprintf("%d", dependency.Index), - }) - session.MakeRequest(t, req, http.StatusSeeOther) - } - removeDependency := func(t *testing.T, issue, dependency api.Issue) { - t.Helper() - - urlStr := fmt.Sprintf("/%s/%s/issues/%d/dependency/delete", owner.Name, repo.Name, issue.Index) - req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ - "_csrf": GetCSRF(t, session, fmt.Sprintf("/%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)), - "removeDependencyID": fmt.Sprintf("%d", dependency.Index), - "dependencyType": "blockedBy", - }) - session.MakeRequest(t, req, http.StatusSeeOther) - } - - assertHasDependency := func(t *testing.T, issueID, dependencyID int64, hasDependency bool) { - t.Helper() - - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/dependencies", owner.Name, repo.Name, issueID) - req := NewRequest(t, "GET", urlStr) - resp := MakeRequest(t, req, http.StatusOK) - - var issues []api.Issue - DecodeJSON(t, resp, &issues) - - if hasDependency { - assert.NotEmpty(t, issues) - assert.EqualValues(t, issues[0].Index, dependencyID) - } else { - assert.Empty(t, issues) - } - } - - t.Run("Add dependency", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - issue1 := createIssue(t, "issue #1") - issue2 := createIssue(t, "issue #2") - addDependency(t, issue1, issue2) - - assertHasDependency(t, issue1.Index, issue2.Index, true) - }) - - t.Run("Remove dependency", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - issue1 := createIssue(t, "issue #1") - issue2 := createIssue(t, "issue #2") - addDependency(t, issue1, issue2) - removeDependency(t, issue1, issue2) - - assertHasDependency(t, issue1.Index, issue2.Index, false) - }) -} - func TestIssueCommentClose(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") @@ -396,16 +308,6 @@ func TestIssueCommentUpdate(t *testing.T) { comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) assert.Equal(t, modifiedContent, comment.Content) - - // make the comment empty - req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{ - "_csrf": GetCSRF(t, session, issueURL), - "content": "", - }) - session.MakeRequest(t, req, http.StatusOK) - - comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) - assert.Equal(t, "", comment.Content) } func TestIssueReaction(t *testing.T) { diff --git a/tests/integration/migration-test/gitea-v1.6.4.mssql.sql.gz b/tests/integration/migration-test/gitea-v1.6.4.mssql.sql.gz new file mode 100644 index 0000000000..1b676feda1 Binary files /dev/null and b/tests/integration/migration-test/gitea-v1.6.4.mssql.sql.gz differ diff --git a/tests/integration/migration-test/gitea-v1.7.0.mssql.sql.gz b/tests/integration/migration-test/gitea-v1.7.0.mssql.sql.gz new file mode 100644 index 0000000000..bd869cfa58 Binary files /dev/null and b/tests/integration/migration-test/gitea-v1.7.0.mssql.sql.gz differ diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index e0e5620cd2..d1aa3f3eb8 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -14,6 +14,7 @@ import ( "path/filepath" "regexp" "sort" + "strings" "testing" "code.gitea.io/gitea/models/db" @@ -257,6 +258,31 @@ func restoreOldDB(t *testing.T, version string) bool { _, err = db.Exec(data) assert.NoError(t, err) db.Close() + + case setting.Database.Type.IsMSSQL(): + host, port := setting.ParseMSSQLHostPort(setting.Database.Host) + db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, "master", setting.Database.User, setting.Database.Passwd)) + assert.NoError(t, err) + defer db.Close() + + _, err = db.Exec("DROP DATABASE IF EXISTS [gitea]") + assert.NoError(t, err) + + statements := strings.Split(data, "\nGO\n") + for _, statement := range statements { + if len(statement) > 5 && statement[:5] == "USE [" { + dbname := statement[5 : len(statement)-1] + db.Close() + db, err = sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, dbname, setting.Database.User, setting.Database.Passwd)) + assert.NoError(t, err) + defer db.Close() + } + _, err = db.Exec(statement) + assert.NoError(t, err, "Failure whilst running: %s\nError: %v", statement, err) + } + db.Close() } return true } diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 46beddb5f3..1da1c6f9c0 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -6,12 +6,9 @@ package integration import ( "bytes" "context" - "crypto/sha256" - "encoding/base64" "fmt" "io" "net/http" - "net/url" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -473,57 +470,6 @@ func TestSignInOAuthCallbackSignIn(t *testing.T) { assert.Greater(t, userAfterLogin.LastLoginUnix, userGitLab.LastLoginUnix) } -func TestSignInOAuthCallbackPKCE(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - // Setup authentication source - gitlabName := "gitlab" - gitlab := addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName)) - // Create a user as if it had been previously been created by the authentication source. - userGitLabUserID := "5678" - userGitLab := &user_model.User{ - Name: "gitlabuser", - Email: "gitlabuser@example.com", - Passwd: "gitlabuserpassword", - Type: user_model.UserTypeIndividual, - LoginType: auth_model.OAuth2, - LoginSource: gitlab.ID, - LoginName: userGitLabUserID, - } - defer createUser(context.Background(), t, userGitLab)() - - // initial redirection (to generate the code_challenge) - session := emptyTestSession(t) - req := NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s", gitlabName)) - resp := session.MakeRequest(t, req, http.StatusTemporaryRedirect) - dest, err := url.Parse(resp.Header().Get("Location")) - assert.NoError(t, err) - assert.Equal(t, "S256", dest.Query().Get("code_challenge_method")) - codeChallenge := dest.Query().Get("code_challenge") - assert.NotEmpty(t, codeChallenge) - - // callback (to check the initial code_challenge) - defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { - codeVerifier := req.URL.Query().Get("code_verifier") - assert.NotEmpty(t, codeVerifier) - assert.Greater(t, len(codeVerifier), 40, codeVerifier) - - sha2 := sha256.New() - io.WriteString(sha2, codeVerifier) - assert.Equal(t, codeChallenge, base64.RawURLEncoding.EncodeToString(sha2.Sum(nil))) - - return goth.User{ - Provider: gitlabName, - UserID: userGitLabUserID, - Email: userGitLab.Email, - }, nil - })() - req = NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s/callback?code=XYZ&state=XYZ", gitlabName)) - resp = session.MakeRequest(t, req, http.StatusSeeOther) - assert.Equal(t, "/", test.RedirectURL(resp)) - unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userGitLab.ID}) -} - func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) { defer tests.PrepareTestEnv(t)() diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index bcdb352612..63ec3f9f35 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -6,16 +6,13 @@ package integration import ( "context" "net/http" - "net/http/httptest" "net/url" - "path" "strconv" "strings" "testing" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - 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/git" @@ -402,82 +399,3 @@ func TestPullView_CodeOwner(t *testing.T) { }) }) } - -func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { - onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - user1Session := loginUser(t, "user1") - user2Session := loginUser(t, "user2") - - // Have user1 create a fork of repo1. - testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1") - - t.Run("Submit approve/reject review on merged PR", func(t *testing.T) { - // Create a merged PR (made by user1) in the upstream repo1. - testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) - - // Grab the CSRF token. - req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4])) - resp = user2Session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - - // Submit an approve review on the PR. - testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity) - - // Submit a reject review on the PR. - testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity) - }) - - t.Run("Submit approve/reject review on closed PR", func(t *testing.T) { - // Created a closed PR (made by user1) in the upstream repo1. - testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n") - resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title") - elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) - testIssueClose(t, user1Session, elem[1], elem[2], elem[4]) - - // Grab the CSRF token. - req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4])) - resp = user2Session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - - // Submit an approve review on the PR. - testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity) - - // Submit a reject review on the PR. - testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity) - }) - }) -} - -func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder { - options := map[string]string{ - "_csrf": csrf, - "commit_id": "", - "content": "test", - "type": reviewType, - } - - submitURL := path.Join(owner, repo, "pulls", pullNumber, "files", "reviews", "submit") - req := NewRequestWithValues(t, "POST", submitURL, options) - return session.MakeRequest(t, req, expectedSubmitStatus) -} - -func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder { - req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber)) - resp := session.MakeRequest(t, req, http.StatusOK) - - htmlDoc := NewHTMLParser(t, resp.Body) - closeURL := path.Join(owner, repo, "issues", issueNumber, "comments") - - options := map[string]string{ - "_csrf": htmlDoc.GetCSRF(), - "status": "close", - } - - req = NewRequestWithValues(t, "POST", closeURL, options) - return session.MakeRequest(t, req, http.StatusOK) -} diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index 80eea34513..26c99e6445 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -12,9 +12,6 @@ import ( "testing" auth_model "code.gitea.io/gitea/models/auth" - git_model "code.gitea.io/gitea/models/git" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -71,6 +68,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { // Update commit status, and check if icon is updated as well for _, status := range statusList { + // Call API to add status for commit t.Run("CreateStatus", doAPICreateCommitStatus(testCtx, commitID, api.CreateStatusOption{ State: status, @@ -92,10 +90,6 @@ func TestPullCreate_CommitStatus(t *testing.T) { assert.True(t, ok) assert.Contains(t, cls, statesIcons[status]) } - - repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) - css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID}) - assert.EqualValues(t, api.CommitStatusWarning, css.State) }) } diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go index ad3c7bf325..acc8cb68c2 100644 --- a/tests/integration/release_test.go +++ b/tests/integration/release_test.go @@ -9,19 +9,15 @@ import ( "testing" "time" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title string, preRelease, draft bool) { @@ -311,34 +307,3 @@ func TestDownloadReleaseAttachment(t *testing.T) { session := loginUser(t, "user2") session.MakeRequest(t, req, http.StatusOK) } - -func TestReleaseHideArchiveLinksUI(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{TagName: "v2.0"}) - - require.NoError(t, release.LoadAttributes(db.DefaultContext)) - - session := loginUser(t, release.Repo.OwnerName) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - zipURL := fmt.Sprintf("%s/archive/%s.zip", release.Repo.Link(), release.TagName) - tarGzURL := fmt.Sprintf("%s/archive/%s.tar.gz", release.Repo.Link(), release.TagName) - - resp := session.MakeRequest(t, NewRequest(t, "GET", release.HTMLURL()), http.StatusOK) - body := resp.Body.String() - assert.Contains(t, body, zipURL) - assert.Contains(t, body, tarGzURL) - - hideArchiveLinks := true - - req := NewRequestWithJSON(t, "PATCH", release.APIURL(), &api.EditReleaseOption{ - HideArchiveLinks: &hideArchiveLinks, - }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusOK) - - resp = session.MakeRequest(t, NewRequest(t, "GET", release.HTMLURL()), http.StatusOK) - body = resp.Body.String() - assert.NotContains(t, body, zipURL) - assert.NotContains(t, body, tarGzURL) -} diff --git a/tests/integration/remote_test.go b/tests/integration/remote_test.go deleted file mode 100644 index d905f88a81..0000000000 --- a/tests/integration/remote_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright Earl Warren -// SPDX-License-Identifier: MIT - -package integration - -import ( - "context" - "fmt" - "net/http" - "testing" - - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/test" - remote_service "code.gitea.io/gitea/services/remote" - "code.gitea.io/gitea/tests" - - "github.com/markbates/goth" - "github.com/stretchr/testify/assert" -) - -func TestRemote_MaybePromoteUserSuccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - // - // OAuth2 authentication source GitLab - // - gitlabName := "gitlab" - _ = addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName)) - // - // Remote authentication source matching the GitLab authentication source - // - remoteName := "remote" - remote := createRemoteAuthSource(t, remoteName, "http://mygitlab.eu", gitlabName) - - // - // Create a user as if it had previously been created by the remote - // authentication source. - // - gitlabUserID := "5678" - gitlabEmail := "gitlabuser@example.com" - userBeforeSignIn := &user_model.User{ - Name: "gitlabuser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: remote.ID, - LoginName: gitlabUserID, - } - defer createUser(context.Background(), t, userBeforeSignIn)() - - // - // A request for user information sent to Goth will return a - // goth.User exactly matching the user created above. - // - defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { - return goth.User{ - Provider: gitlabName, - UserID: gitlabUserID, - Email: gitlabEmail, - }, nil - })() - req := NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s/callback?code=XYZ&state=XYZ", gitlabName)) - resp := MakeRequest(t, req, http.StatusSeeOther) - assert.Equal(t, "/", test.RedirectURL(resp)) - userAfterSignIn := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userBeforeSignIn.ID}) - - // both are about the same user - assert.Equal(t, userAfterSignIn.ID, userBeforeSignIn.ID) - // the login time was updated, proof the login succeeded - assert.Greater(t, userAfterSignIn.LastLoginUnix, userBeforeSignIn.LastLoginUnix) - // the login type was promoted from Remote to OAuth2 - assert.Equal(t, userBeforeSignIn.LoginType, auth_model.Remote) - assert.Equal(t, userAfterSignIn.LoginType, auth_model.OAuth2) - // the OAuth2 email was used to set the missing user email - assert.Equal(t, userBeforeSignIn.Email, "") - assert.Equal(t, userAfterSignIn.Email, gitlabEmail) -} - -func TestRemote_MaybePromoteUserFail(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - ctx := context.Background() - // - // OAuth2 authentication source GitLab - // - gitlabName := "gitlab" - gitlabSource := addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName)) - // - // Remote authentication source matching the GitLab authentication source - // - remoteName := "remote" - remoteSource := createRemoteAuthSource(t, remoteName, "http://mygitlab.eu", gitlabName) - - { - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, &auth_model.Source{}, "", "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonNotAuth2, reason) - } - - { - remoteSource.Type = auth_model.OAuth2 - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, remoteSource, "", "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonBadAuth2, reason) - remoteSource.Type = auth_model.Remote - } - - { - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, "unknownloginname", "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonLoginNameNotExists, reason) - } - - { - remoteUserID := "844" - remoteUser := &user_model.User{ - Name: "withmailuser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: remoteSource.ID, - LoginName: remoteUserID, - Email: "some@example.com", - } - defer createUser(context.Background(), t, remoteUser)() - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonEmailIsSet, reason) - } - - { - remoteUserID := "7464" - nonexistentloginsource := int64(4344) - remoteUser := &user_model.User{ - Name: "badsourceuser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: nonexistentloginsource, - LoginName: remoteUserID, - } - defer createUser(context.Background(), t, remoteUser)() - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonNoSource, reason) - } - - { - remoteUserID := "33335678" - remoteUser := &user_model.User{ - Name: "badremoteuser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: gitlabSource.ID, - LoginName: remoteUserID, - } - defer createUser(context.Background(), t, remoteUser)() - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonSourceWrongType, reason) - } - - { - unrelatedName := "unrelated" - unrelatedSource := addAuthSource(t, authSourcePayloadGitHubCustom(unrelatedName)) - assert.NotNil(t, unrelatedSource) - - remoteUserID := "488484" - remoteEmail := "4848484@example.com" - remoteUser := &user_model.User{ - Name: "unrelateduser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: remoteSource.ID, - LoginName: remoteUserID, - } - defer createUser(context.Background(), t, remoteUser)() - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, unrelatedSource, remoteUserID, remoteEmail) - assert.NoError(t, err) - assert.False(t, promoted) - assert.Equal(t, remote_service.ReasonNoMatch, reason) - } - - { - remoteUserID := "5678" - remoteEmail := "gitlabuser@example.com" - remoteUser := &user_model.User{ - Name: "remoteuser", - Type: user_model.UserTypeRemoteUser, - LoginType: auth_model.Remote, - LoginSource: remoteSource.ID, - LoginName: remoteUserID, - } - defer createUser(context.Background(), t, remoteUser)() - promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, remoteEmail) - assert.NoError(t, err) - assert.True(t, promoted) - assert.Equal(t, remote_service.ReasonPromoted, reason) - } -} diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index b98fce0f4a..9a278e706d 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -227,19 +227,19 @@ func TestWebhookForms(t *testing.T) { })) t.Run("matrix/required", testWebhookForms("matrix", session, map[string]string{ - "homeserver_url": "https://matrix.example.com", - "access_token": "123456", - "room_id": "123", + "homeserver_url": "https://matrix.example.com", + "room_id": "123", + "authorization_header": "Bearer 123456", }, map[string]string{ - "access_token": "", + "authorization_header": "", })) t.Run("matrix/optional", testWebhookForms("matrix", session, map[string]string{ "homeserver_url": "https://matrix.example.com", - "access_token": "123456", "room_id": "123", "message_type": "1", // m.text - "branch_filter": "matrix/*", + "branch_filter": "matrix/*", + "authorization_header": "Bearer 123456", })) t.Run("wechatwork/required", testWebhookForms("wechatwork", session, map[string]string{ @@ -267,12 +267,14 @@ func TestWebhookForms(t *testing.T) { })) t.Run("sourcehut_builds/required", testWebhookForms("sourcehut_builds", session, map[string]string{ - "payload_url": "https://sourcehut_builds.example.com", - "manifest_path": ".build.yml", - "visibility": "PRIVATE", - "access_token": "123456", + "payload_url": "https://sourcehut_builds.example.com", + "manifest_path": ".build.yml", + "visibility": "PRIVATE", + "authorization_header": "Bearer 123456", }, map[string]string{ - "access_token": "", + "authorization_header": "", + }, map[string]string{ + "authorization_header": "token ", }, map[string]string{ "manifest_path": "", }, map[string]string{ @@ -287,9 +289,9 @@ func TestWebhookForms(t *testing.T) { "manifest_path": ".build.yml", "visibility": "PRIVATE", "secrets": "on", - "access_token": "123456", - "branch_filter": "srht/*", + "branch_filter": "srht/*", + "authorization_header": "Bearer 123456", })) } diff --git a/tests/integration/size_translations_test.go b/tests/integration/size_translations_test.go index 1ee5f7b36f..78cd16795d 100644 --- a/tests/integration/size_translations_test.go +++ b/tests/integration/size_translations_test.go @@ -89,7 +89,7 @@ func TestDataSizeTranslation(t *testing.T) { fullSize = noDigits.ReplaceAllString(fullSize, "") assert.Equal(t, "git: КиБ; lfs: Б", fullSize) - // Check if file sizes are correctly translated + // Check if file sizes are correclty translated testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/137byteFile.txt"), "137 Б") testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/1.5kibFile.txt"), "1,5 КиБ") testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/1.25mibFile.txt"), "1,3 МиБ") diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index 02cc9b51cc..3a3932bdc2 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -495,7 +495,9 @@ func TestUserPronouns(t *testing.T) { // Set the pronouns for user2 pronouns := "she/her" req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{ - Pronouns: &pronouns, + LoginName: "user2", + SourceID: 0, + Pronouns: &pronouns, }).AddTokenAuth(adminToken) resp := MakeRequest(t, req, http.StatusOK) @@ -594,7 +596,9 @@ func TestUserPronouns(t *testing.T) { // Set the pronouns to Unspecified (an empty string) via the API pronouns := "" req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{ - Pronouns: &pronouns, + LoginName: "user2", + SourceID: 0, + Pronouns: &pronouns, }).AddTokenAuth(adminToken) MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/webfinger_test.go b/tests/integration/webfinger_test.go index 825cffed7a..55fb211779 100644 --- a/tests/integration/webfinger_test.go +++ b/tests/integration/webfinger_test.go @@ -66,19 +66,4 @@ func TestWebfinger(t *testing.T) { req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=mailto:%s", user.Email)) MakeRequest(t, req, http.StatusNotFound) - - req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=https://%s/%s/", appURL.Host, user.Name)) - session.MakeRequest(t, req, http.StatusOK) - - req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=https://%s/%s", appURL.Host, user.Name)) - session.MakeRequest(t, req, http.StatusOK) - - req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=http://%s/%s/foo", appURL.Host, user.Name)) - session.MakeRequest(t, req, http.StatusNotFound) - - req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=http://%s", appURL.Host)) - MakeRequest(t, req, http.StatusNotFound) - - req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=http://%s/%s/foo", "example.com", user.Name)) - MakeRequest(t, req, http.StatusBadRequest) } diff --git a/tests/mssql.ini.tmpl b/tests/mssql.ini.tmpl new file mode 100644 index 0000000000..9346f75874 --- /dev/null +++ b/tests/mssql.ini.tmpl @@ -0,0 +1,107 @@ +APP_NAME = Gitea: Git with a cup of tea +RUN_MODE = prod + +[database] +DB_TYPE = mssql +HOST = {{TEST_MSSQL_HOST}} +NAME = {{TEST_MSSQL_DBNAME}} +USER = {{TEST_MSSQL_USERNAME}} +PASSWD = {{TEST_MSSQL_PASSWORD}} +SSL_MODE = disable + +[indexer] +REPO_INDEXER_ENABLED = true +REPO_INDEXER_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/indexers/repos.bleve + +[queue.issue_indexer] +TYPE = level +DATADIR = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/indexers/issues.queue + +[queue] +TYPE = immediate + +[repository] +ROOT = {{REPO_TEST_DIR}}tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/gitea-repositories + +[repository.local] +LOCAL_COPY_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/tmp/local-repo + +[repository.upload] +TEMP_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/tmp/uploads + +[repository.signing] +SIGNING_KEY = none + +[server] +SSH_DOMAIN = localhost +HTTP_PORT = 3003 +ROOT_URL = http://localhost:3003/ +DISABLE_SSH = false +SSH_LISTEN_HOST = localhost +SSH_PORT = 2201 +START_SSH_SERVER = true +LFS_START_SERVER = true +OFFLINE_MODE = false +LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w +APP_DATA_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data +BUILTIN_SSH_SERVER_USER = git +SSH_TRUSTED_USER_CA_KEYS = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb4DC1dMFnJ6pXWo7GMxTchtzmJHYzfN6sZ9FAPFR4ijMLfGki+olvOMO5Fql1/yGnGfbELQa1S6y4shSvj/5K+zUFScmEXYf3Gcr87RqilLkyk16RS+cHNB1u87xTHbETaa3nyCJeGQRpd4IQ4NKob745mwDZ7jQBH8AZEng50Oh8y8fi8skBBBzaYp1ilgvzG740L7uex6fHV62myq0SXeCa+oJUjq326FU8y+Vsa32H8A3e7tOgXZPdt2TVNltx2S9H2WO8RMi7LfaSwARNfy1zu+bfR50r6ef8Yx5YKCMz4wWb1SHU1GS800mjOjlInLQORYRNMlSwR1+vLlVDciOqFapDSbj+YOVOawR0R1aqlSKpZkt33DuOBPx9qe6CVnIi7Z+Px/KqM+OLCzlLY/RS+LbxQpDWcfTVRiP+S5qRTcE3M3UioN/e0BE/1+MpX90IGpvVkA63ILYbKEa4bM3ASL7ChTCr6xN5XT+GpVJveFKK1cfNx9ExHI4rzYE= + +[attachment] +PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data/attachments + +[mailer] +ENABLED = true +PROTOCOL = dummy +FROM = mssql-{{TEST_TYPE}}-test@gitea.io + +[service] +REGISTER_EMAIL_CONFIRM = false +REGISTER_MANUAL_CONFIRM = false +DISABLE_REGISTRATION = false +ENABLE_CAPTCHA = false +REQUIRE_SIGNIN_VIEW = false +DEFAULT_KEEP_EMAIL_PRIVATE = false +DEFAULT_ALLOW_CREATE_ORGANIZATION = true +NO_REPLY_ADDRESS = noreply.example.org +ENABLE_NOTIFY_MAIL = true + +[picture] +DISABLE_GRAVATAR = false +ENABLE_FEDERATED_AVATAR = false +AVATAR_UPLOAD_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data/repo-avatars + +[session] +PROVIDER = file +PROVIDER_CONFIG = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data/sessions + +[log] +MODE = {{TEST_LOGGER}} +ROOT_PATH = {{REPO_TEST_DIR}}mssql-log +ENABLE_SSH_LOG = true +logger.xorm.MODE = file + +[log.test] +LEVEL = Info +COLORIZE = true + +[log.file] +LEVEL = Debug + +[security] +PASSWORD_HASH_ALGO = argon2 +DISABLE_GIT_HOOKS = false +INSTALL_LOCK = true +SECRET_KEY = 9pCviYTWSb +INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true + +[lfs] +PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/data/lfs + +[packages] +ENABLED = true + +[actions] +ENABLED = true diff --git a/tests/test_utils.go b/tests/test_utils.go index a607194bef..5837e1263c 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -165,6 +165,18 @@ func InitTest(requireGitea bool) { log.Fatal("db.Exec: CREATE SCHEMA: %v", err) } } + + case setting.Database.Type.IsMSSQL(): + host, port := setting.ParseMSSQLHostPort(setting.Database.Host) + db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, "master", setting.Database.User, setting.Database.Passwd)) + if err != nil { + log.Fatal("sql.Open: %v", err) + } + if _, err := db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil { + log.Fatal("db.Exec: %v", err) + } + defer db.Close() } routers.InitWebInstalled(graceful.GetManager().HammerContext()) diff --git a/web_src/css/base.css b/web_src/css/base.css index 3d91586934..0ab923594c 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -59,7 +59,7 @@ html, body { } body { - line-height: 20px; + line-height: 1.4285rem; font-family: var(--fonts-regular); color: var(--color-text); background-color: var(--color-body); @@ -246,18 +246,6 @@ h1.error-code { user-select: none; } -.top-right-buttons { - gap: 0.5rem; -} - -.top-right-buttons .ui.button { - margin-right: 0; -} - -.ui.partial.secondary.menu { - margin-bottom: 0; -} - a { color: var(--color-primary); cursor: pointer; @@ -343,6 +331,61 @@ a.label, background-color: var(--color-label-bg); } +/* fix Fomantic's line-height cutting off "g" on Windows Chrome with Segoe UI */ +.ui.input > input { + line-height: var(--line-height-default); + text-align: start; /* Override fomantic's `text-align: left` to make RTL work via HTML `dir="auto"` */ +} + +/* fix Fomantic's line-height causing vertical scrollbars to appear */ +ul.ui.list li, +ol.ui.list li, +.ui.list > .item, +.ui.list .list > .item { + line-height: var(--line-height-default); +} + +.ui.input.focus > input, +.ui.input > input:focus { + border-color: var(--color-primary); +} + +.ui.action.input .ui.ui.button { + border-color: var(--color-input-border); + padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */ + padding-bottom: 0; +} + +/* currently used for search bar dropdowns in repo search and explore code */ +.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection { + min-width: 10em; +} +.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) { + border-right: none; +} +.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover { + border-color: var(--color-input-border); +} +.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible { + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} +.ui.action.input:not([class*="left action"]) > input, +.ui.action.input:not([class*="left action"]) > input:hover { + border-right: none; +} +.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection, +.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover, +.ui.action.input:not([class*="left action"]) > input:focus + .button, +.ui.action.input:not([class*="left action"]) > input:focus + .button:hover, +.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button, +.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover { + border-left-color: var(--color-primary); +} +.ui.action.input:not([class*="left action"]) > input:focus { + border-right-color: var(--color-primary); +} + .ui.menu { display: flex; } @@ -487,6 +530,21 @@ a.label, color: var(--color-text-light-2); } +.ui.list .list > .item .header, +.ui.list > .item .header { + color: var(--color-text-dark); +} + +.ui.list .list > .item > .content, +.ui.list > .item > .content { + color: var(--color-text); +} + +.ui.list .list > .item .description, +.ui.list > .item .description { + color: var(--color-text); +} + /* replace item margin on secondary menu items with gap and remove both the negative margins on the menu as well as margin on the items */ .ui.secondary.menu { @@ -605,6 +663,10 @@ img.ui.avatar, aspect-ratio: 1; } +.ui.divided.list > .item { + border-color: var(--color-secondary); +} + .ui.error.message .header, .ui.warning.message .header { color: inherit; @@ -857,6 +919,14 @@ input:-webkit-autofill:active, text-align: right !important; } +.ui .text.normal { + font-weight: var(--font-weight-normal); +} + +.ui .text.italic { + font-style: italic; +} + .ui .text.truncate { overflow-x: hidden; text-overflow: ellipsis; @@ -864,6 +934,14 @@ input:-webkit-autofill:active, display: inline-block; } +.ui .text.thin { + font-weight: var(--font-weight-normal); +} + +.ui .text.middle { + vertical-align: middle; +} + .ui .message.flash-message { text-align: center; } @@ -1399,6 +1477,11 @@ table th[data-sortt-desc] .svg { vertical-align: -0.15em; } +/* for the jquery.minicolors plugin */ +.minicolors-panel { + background: var(--color-secondary-dark-1) !important; +} + .ui.tabular.menu { border-color: var(--color-secondary); } @@ -1547,7 +1630,6 @@ table th[data-sortt-desc] .svg { align-items: center; gap: .25rem; vertical-align: middle; - min-width: 0; } .ui.ui.button { @@ -1563,10 +1645,19 @@ table th[data-sortt-desc] .svg { align-items: stretch; } +.ui.ui.icon.input .icon { + display: flex; + align-items: center; + justify-content: center; +} + +.ui.icon.input > i.icon { + transition: none; +} + .flex-items-block > .item, .flex-text-block { display: flex; align-items: center; gap: .25rem; - min-width: 0; } diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css index 2ee2399d73..4bb9fa38bf 100644 --- a/web_src/css/dashboard.css +++ b/web_src/css/dashboard.css @@ -7,6 +7,7 @@ .dashboard.feeds .context.user.menu .ui.header, .dashboard.issues .context.user.menu .ui.header { font-size: 1rem; + text-transform: none; } .dashboard.feeds .filter.menu, diff --git a/web_src/css/features/colorpicker.css b/web_src/css/features/colorpicker.css deleted file mode 100644 index b7436783df..0000000000 --- a/web_src/css/features/colorpicker.css +++ /dev/null @@ -1,47 +0,0 @@ -.js-color-picker-input { - display: flex; - position: relative; -} - -.js-color-picker-input input { - padding-top: 8px !important; - padding-bottom: 8px !important; - padding-left: 32px !important; -} - -.js-color-picker-input .preview-square { - position: absolute; - aspect-ratio: 1; - height: 16px; - left: 10px; - top: 50%; - transform: translateY(-50%); - border-radius: 2px; - background: repeating-linear-gradient(45deg, #aaa 25%, transparent 25%, transparent 75%, #aaa 75%, #aaa), repeating-linear-gradient(45deg, #aaa 25%, #fff 25%, #fff 75%, #aaa 75%, #aaa); /* stylelint-disable-line scale-unlimited/declaration-strict-value */ - background-position: 0 0, 4px 4px; - background-size: 8px 8px; -} - -.js-color-picker-input .preview-square::after { - content: ""; - position: absolute; - width: 100%; - height: 100%; - border-radius: inherit; - background-color: currentcolor; -} - -hex-color-picker { - width: 180px; - height: 120px; -} - -hex-color-picker::part(hue-pointer), -hex-color-picker::part(saturation-pointer) { - width: 22px; - height: 22px; -} - -hex-color-picker::part(hue) { - flex-basis: 16px; -} diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css index e23c146748..30df994c38 100644 --- a/web_src/css/features/projects.css +++ b/web_src/css/features/projects.css @@ -22,27 +22,34 @@ cursor: default; } -.project-column .issue-card { - color: var(--color-text); -} - .project-column-header { display: flex; align-items: center; justify-content: space-between; } +.project-column-header.dark-label { + color: var(--color-project-board-dark-label) !important; +} + +.project-column-header.dark-label .project-column-title { + color: var(--color-project-board-dark-label) !important; +} + +.project-column-header.light-label { + color: var(--color-project-board-light-label) !important; +} + +.project-column-header.light-label .project-column-title { + color: var(--color-project-board-light-label) !important; +} + .project-column-title { background: none !important; line-height: 1.25 !important; cursor: inherit; } -.project-column-title, -.project-column-issue-count { - color: inherit !important; -} - .project-column > .cards { flex: 1; display: flex; @@ -57,8 +64,6 @@ .project-column > .divider { margin: 5px 0; - border-color: currentcolor; - opacity: .5; } .project-column:first-child { @@ -97,3 +102,26 @@ .card-ghost * { opacity: 0; } + +.color-field .minicolors.minicolors-theme-default { + display: block; +} + +.color-field .minicolors.minicolors-theme-default .minicolors-input { + height: 38px; + padding-left: 2rem; +} + +.color-field .minicolors.minicolors-theme-default .minicolors-swatch { + top: 10px; +} + +.edit-project-column-modal .color.picker.column, +.new-project-column-modal .color.picker.column { + display: flex; +} + +.edit-project-column-modal .color.picker.column .minicolors, +.new-project-column-modal .color.picker.column .minicolors { + flex: 1; +} diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index 118c058b19..13962f19d7 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -63,20 +63,3 @@ only use: display: none !important; } } - -.tab-size-1 { tab-size: 1 !important; } -.tab-size-2 { tab-size: 2 !important; } -.tab-size-3 { tab-size: 3 !important; } -.tab-size-4 { tab-size: 4 !important; } -.tab-size-5 { tab-size: 5 !important; } -.tab-size-6 { tab-size: 6 !important; } -.tab-size-7 { tab-size: 7 !important; } -.tab-size-8 { tab-size: 8 !important; } -.tab-size-9 { tab-size: 9 !important; } -.tab-size-10 { tab-size: 10 !important; } -.tab-size-11 { tab-size: 11 !important; } -.tab-size-12 { tab-size: 12 !important; } -.tab-size-13 { tab-size: 13 !important; } -.tab-size-14 { tab-size: 14 !important; } -.tab-size-15 { tab-size: 15 !important; } -.tab-size-16 { tab-size: 16 !important; } diff --git a/web_src/css/index.css b/web_src/css/index.css index 49ceb2c8ce..445b20dc12 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -6,9 +6,7 @@ @import "./modules/container.css"; @import "./modules/divider.css"; @import "./modules/header.css"; -@import "./modules/input.css"; @import "./modules/label.css"; -@import "./modules/list.css"; @import "./modules/segment.css"; @import "./modules/grid.css"; @import "./modules/message.css"; diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index 361618c449..0f78ad25cb 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -34,14 +34,10 @@ border-radius: var(--border-radius-circle); } -.is-loading.loading-icon-2px::after { +.is-loading.small-loading-icon::after { border-width: 2px; } -.is-loading.loading-icon-3px::after { - border-width: 3px; -} - /* for single form button, the loading state should be on the button, but not go semi-transparent, just replace the text on the button with the loader. */ form.single-button-form.is-loading > * { opacity: 1; @@ -66,7 +62,7 @@ form.single-button-form.is-loading .button { background: transparent; } -/* TODO: not needed, use "is-loading loading-icon-2px" instead */ +/* TODO: not needed, use "is-loading small-loading-icon" instead */ code.language-math.is-loading::after { padding: 0; border-width: 2px; diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css index acc8408f37..48560bd3d9 100644 --- a/web_src/css/modules/divider.css +++ b/web_src/css/modules/divider.css @@ -2,16 +2,12 @@ margin: 10px 0; height: 0; font-weight: var(--font-weight-medium); + text-transform: uppercase; color: var(--color-text); font-size: 1rem; width: 100%; } -h4.divider { - margin-top: 1.25rem; - margin-bottom: 1.25rem; -} - .divider:not(.divider-text) { border-top: 1px solid var(--color-secondary); } diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 5d4e12cc12..1ca513687f 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -6,16 +6,10 @@ margin-top: var(--page-spacing); } -/* small options menu on the left, used in settings/admin pages */ .flex-container-nav { width: 240px; } -/* wide sidebar on the right, used in frontpage */ -.flex-container-sidebar { - width: 35%; -} - .flex-container-main { flex: 1; min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */ @@ -25,9 +19,7 @@ .flex-container { flex-direction: column; } - .flex-container-nav, - .flex-container-sidebar { - order: -1; + .flex-container-nav { width: auto; } } diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css index 9cec5fcbe6..05381e1185 100644 --- a/web_src/css/modules/header.css +++ b/web_src/css/modules/header.css @@ -9,6 +9,7 @@ font-family: var(--fonts-regular); font-weight: var(--font-weight-medium); line-height: 1.28571429; + text-transform: none; } .ui.header:first-child { diff --git a/web_src/css/modules/input.css b/web_src/css/modules/input.css deleted file mode 100644 index 18b785ac82..0000000000 --- a/web_src/css/modules/input.css +++ /dev/null @@ -1,197 +0,0 @@ -/* based on Fomantic UI input module, with just the parts extracted that we use. If you find any - unused rules here after refactoring, please remove them. */ - -.ui.input { - position: relative; - font-weight: var(--font-weight-normal); - display: inline-flex; - color: var(--color-input-text); -} -.ui.input > input { - margin: 0; - max-width: 100%; - flex: 1 0 auto; - outline: none; - font-family: var(--fonts-regular); - padding: 0.67857143em 1em; - border: 1px solid var(--color-input-border); - color: var(--color-input-text); - border-radius: 0.28571429rem; - line-height: var(--line-height-default); - text-align: start; -} - -.ui.disabled.input, -.ui.input:not(.disabled) input[disabled] { - opacity: var(--opacity-disabled); -} -.ui.disabled.input > input, -.ui.input:not(.disabled) input[disabled] { - pointer-events: none; -} - -.ui.input.focus > input, -.ui.input > input:focus { - border-color: var(--color-primary); -} - -.ui.input.error > input { - background: var(--color-error-bg); - border-color: var(--color-error-border); - color: var(--color-error-text); -} - -.ui.icon.input > i.icon { - display: flex; - align-items: center; - justify-content: center; - cursor: default; - position: absolute; - text-align: center; - top: 0; - right: 0; - margin: 0; - height: 100%; - width: 2.67142857em; - opacity: 0.5; - border-radius: 0 0.28571429rem 0.28571429rem 0; - pointer-events: none; - padding: 4px; -} - -.ui.icon.input > i.icon.is-loading { - position: absolute !important; - height: 28px; - top: 4px; -} - -.ui.icon.input > i.icon.is-loading > * { - visibility: hidden; -} - -.ui.ui.ui.ui.icon.input > textarea, -.ui.ui.ui.ui.icon.input > input { - padding-right: 2.67142857em; -} -.ui.icon.input > i.link.icon { - cursor: pointer; -} -.ui.icon.input > i.circular.icon { - top: 0.35em; - right: 0.5em; -} - -.ui[class*="left icon"].input > i.icon { - right: auto; - left: 1px; - border-radius: 0.28571429rem 0 0 0.28571429rem; -} -.ui[class*="left icon"].input > i.circular.icon { - right: auto; - left: 0.5em; -} -.ui.ui.ui.ui[class*="left icon"].input > textarea, -.ui.ui.ui.ui[class*="left icon"].input > input { - padding-left: 2.67142857em; - padding-right: 1em; -} - -.ui.icon.input > textarea:focus ~ .icon, -.ui.icon.input > input:focus ~ .icon { - opacity: 1; -} - -.ui.icon.input > textarea ~ i.icon { - height: 3em; -} - -.ui.form .field.error > .ui.action.input > .ui.button, -.ui.action.input.error > .ui.button { - border-top: 1px solid var(--color-error-border); - border-bottom: 1px solid var(--color-error-border); -} - -.ui.action.input > .button, -.ui.action.input > .buttons { - display: flex; - align-items: center; - flex: 0 0 auto; -} -.ui.action.input > .button, -.ui.action.input > .buttons > .button { - padding-top: 0.78571429em; - padding-bottom: 0.78571429em; - margin: 0; -} - -.ui.action.input:not([class*="left action"]) > input { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-right-color: transparent; -} - -.ui.action.input > .dropdown:first-child, -.ui.action.input > .button:first-child, -.ui.action.input > .buttons:first-child > .button { - border-radius: 0.28571429rem 0 0 0.28571429rem; -} -.ui.action.input > .dropdown:not(:first-child), -.ui.action.input > .button:not(:first-child), -.ui.action.input > .buttons:not(:first-child) > .button { - border-radius: 0; -} -.ui.action.input > .dropdown:last-child, -.ui.action.input > .button:last-child, -.ui.action.input > .buttons:last-child > .button { - border-radius: 0 0.28571429rem 0.28571429rem 0; -} - -.ui.fluid.input { - display: flex; -} -.ui.fluid.input > input { - width: 0 !important; -} - -.ui.tiny.input { - font-size: 0.85714286em; -} -.ui.small.input { - font-size: 0.92857143em; -} - -.ui.action.input .ui.ui.button { - border-color: var(--color-input-border); - padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */ - padding-bottom: 0; -} - -/* currently used for search bar dropdowns in repo search and explore code */ -.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection { - min-width: 10em; -} -.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) { - border-right: none; -} -.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover { - border-color: var(--color-input-border); -} -.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible { - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} -.ui.action.input:not([class*="left action"]) > input, -.ui.action.input:not([class*="left action"]) > input:hover { - border-right: none; -} -.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection, -.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover, -.ui.action.input:not([class*="left action"]) > input:focus + .button, -.ui.action.input:not([class*="left action"]) > input:focus + .button:hover, -.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button, -.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover { - border-left-color: var(--color-primary); -} -.ui.action.input:not([class*="left action"]) > input:focus { - border-right-color: var(--color-primary); -} diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css index 2032b2c84b..0512c5fddb 100644 --- a/web_src/css/modules/label.css +++ b/web_src/css/modules/label.css @@ -5,12 +5,12 @@ display: inline-flex; align-items: center; gap: .25rem; - min-width: 0; vertical-align: middle; line-height: 1; background: var(--color-label-bg); color: var(--color-label-text); padding: 0.3em 0.5em; + text-transform: none; font-size: 0.85714286rem; font-weight: var(--font-weight-medium); border: 0 solid transparent; diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css deleted file mode 100644 index 32c71e802b..0000000000 --- a/web_src/css/modules/list.css +++ /dev/null @@ -1,193 +0,0 @@ -/* based on Fomantic UI list module, with just the parts extracted that we use. If you find any - unused rules here after refactoring, please remove them. */ - -.ui.list { - list-style-type: none; - margin: 1em 0; - padding: 0; - font-size: 1em; -} - -.ui.list:first-child { - margin-top: 0; - padding-top: 0; -} - -.ui.list:last-child { - margin-bottom: 0; - padding-bottom: 0; -} - -.ui.list > .item, -.ui.list .list > .item { - display: list-item; - table-layout: fixed; - list-style-type: none; - list-style-position: outside; -} - -.ui.list > .list > .item::after, -.ui.list > .item::after { - content: ""; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -.ui.list .list:not(.icon) { - clear: both; - margin: 0; - padding: 0.75em 0 0.25em 0.5em; -} - -.ui.list .list > .item { - padding: 0.14285714em 0; -} - -.ui.list .list > .item > i.icon, -.ui.list > .item > i.icon { - display: table-cell; - min-width: 1.55em; - padding-top: 0; - transition: color 0.1s ease; - padding-right: 0.28571429em; - vertical-align: top; -} -.ui.list .list > .item > i.icon:only-child, -.ui.list > .item > i.icon:only-child { - display: inline-block; - min-width: auto; - vertical-align: top; -} - -.ui.list .list > .item > .image, -.ui.list > .item > .image { - display: table-cell; - background-color: transparent; - vertical-align: top; -} -.ui.list .list > .item > .image:not(:only-child):not(img), -.ui.list > .item > .image:not(:only-child):not(img) { - padding-right: 0.5em; -} -.ui.list .list > .item > .image img, -.ui.list > .item > .image img { - vertical-align: top; -} -.ui.list .list > .item > img.image, -.ui.list .list > .item > .image:only-child, -.ui.list > .item > img.image, -.ui.list > .item > .image:only-child { - display: inline-block; -} - -.ui.list .list > .item > .content, -.ui.list > .item > .content { - color: var(--color-text); -} -.ui.list .list > .item > .image + .content, -.ui.list .list > .item > i.icon + .content, -.ui.list > .item > .image + .content, -.ui.list > .item > i.icon + .content { - display: table-cell; - width: 100%; - padding: 0 0 0 0.5em; - vertical-align: top; -} -.ui.list .list > .item > img.image + .content, -.ui.list > .item > img.image + .content { - display: inline-block; - width: auto; -} -.ui.list .list > .item > .content > .list, -.ui.list > .item > .content > .list { - margin-left: 0; - padding-left: 0; -} - -.ui.list .list > .item .header, -.ui.list > .item .header { - display: block; - margin: 0; - font-family: var(--fonts-regular); - font-weight: var(--font-weight-medium); - color: var(--color-text-dark); -} - -.ui.list .list > .item .description, -.ui.list > .item .description { - display: block; - color: var(--color-text); -} - -.ui.list > .item a, -.ui.list .list > .item a { - cursor: pointer; -} - -.ui.list .list > .item [class*="right floated"], -.ui.list > .item [class*="right floated"] { - float: right; - margin: 0 0 0 1em; -} - -.ui.menu .ui.list > .item, -.ui.menu .ui.list .list > .item { - display: list-item; - table-layout: fixed; - background-color: transparent; - list-style-type: none; - list-style-position: outside; - padding: 0.21428571em 0; -} -.ui.menu .ui.list .list > .item::before, -.ui.menu .ui.list > .item::before { - border: none; - background: none; -} -.ui.menu .ui.list .list > .item:first-child, -.ui.menu .ui.list > .item:first-child { - padding-top: 0; -} -.ui.menu .ui.list .list > .item:last-child, -.ui.menu .ui.list > .item:last-child { - padding-bottom: 0; -} - -.ui.list .list > .disabled.item, -.ui.list > .disabled.item { - pointer-events: none; - opacity: var(--opacity-disabled); -} - -.ui.list .list > a.item:hover > .icons, -.ui.list > a.item:hover > .icons, -.ui.list .list > a.item:hover > i.icon, -.ui.list > a.item:hover > i.icon { - color: var(--color-text-dark); -} - -.ui.divided.list > .item { - border-top: 1px solid var(--color-secondary); -} -.ui.divided.list .list > .item { - border-top: none; -} -.ui.divided.list .item .list > .item { - border-top: none; -} -.ui.divided.list .list > .item:first-child, -.ui.divided.list > .item:first-child { - border-top: none; -} -.ui.divided.list .list > .item:first-child { - border-top-width: 1px; -} - -.ui.relaxed.list > .item:not(:first-child) { - padding-top: 0.42857143em; -} -.ui.relaxed.list > .item:not(:last-child) { - padding-bottom: 0.42857143em; -} diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css index 04543f0986..d4eaf633d7 100644 --- a/web_src/css/modules/segment.css +++ b/web_src/css/modules/segment.css @@ -153,11 +153,6 @@ border-top: none; } -.ui.attached.segment:has(+ .ui[class*="top attached"].header), -.ui.attached.segment:last-child { - border-radius: 0 0 0.28571429rem 0.28571429rem; -} - .ui[class*="top attached"].segment { bottom: 0; margin-bottom: 0; diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css index 6ac7c37d93..76d36b4293 100644 --- a/web_src/css/modules/tippy.css +++ b/web_src/css/modules/tippy.css @@ -29,17 +29,6 @@ z-index: 1; } -/* bare theme, no styling at all, except box-shadow */ -.tippy-box[data-theme="bare"] { - border: none; - box-shadow: 0 6px 18px var(--color-shadow); -} - -.tippy-box[data-theme="bare"] .tippy-content { - padding: 0; - background: transparent; -} - /* tooltip theme for text tooltips */ .tippy-box[data-theme="tooltip"] { diff --git a/web_src/css/repo.css b/web_src/css/repo.css index bf6bfd464b..0093cb3eb5 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -402,7 +402,7 @@ td .commit-summary { .pdf-content { width: 100%; - height: 100vh; + height: 600px; border: none !important; display: flex; align-items: center; @@ -594,19 +594,27 @@ td .commit-summary { align-items: center; } -.repository.view.issue .top-right-buttons { +.repository.view.issue .issue-title-buttons, +.repository.view.issue .edit-buttons { display: flex; } +.issue-title-buttons { + gap: 0.5rem; +} + @media (max-width: 767.98px) { .repository.view.issue .issue-title { flex-direction: column; } - .repository.view.issue .top-right-buttons { + .repository.view.issue .issue-title-buttons, + .repository.view.issue .edit-buttons { width: 100%; - margin-top: .5rem; justify-content: space-between; } + .repository.view.issue .edit-buttons { + margin-top: .5rem; + } .comment.form .issue-content-left .avatar { display: none; } @@ -619,10 +627,6 @@ td .commit-summary { .comment.form .content .form::after { display: none; } - - .repository.view.issue .issue-title.edit-active h1 { - padding-right: 0; - } } .repository.view.issue .issue-title { @@ -640,7 +644,7 @@ td .commit-summary { font-size: 32px; line-height: 40px; margin: 0; - padding-right: 0.5rem; + padding-right: 0.25rem; min-height: var(--repo-header-issue-min-height); } @@ -835,53 +839,55 @@ td .commit-summary { margin-right: 0.25em; } -.singular-commit { - display: flex; - align-items: center; +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit { + line-height: 34px; /* this must be same as .badge height, to avoid overflow */ + clear: both; /* reset the "float right shabox", in the future, use flexbox instead */ } -.singular-commit .badge { - height: 30px !important; +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > img.avatar, +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > .avatar img { + position: relative; + top: -2px; } -.singular-commit .shabox .sha.label { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label { margin: 0; border: 1px solid var(--color-light-border); } -.singular-commit .shabox .sha.label.isSigned.isWarning { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning { border: 1px solid var(--color-red-badge); background: var(--color-red-badge-bg); } -.singular-commit .shabox .sha.label.isSigned.isWarning:hover { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning:hover { background: var(--color-red-badge-hover-bg) !important; } -.singular-commit .shabox .sha.label.isSigned.isVerified { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified { border: 1px solid var(--color-green-badge); background: var(--color-green-badge-bg); } -.singular-commit .shabox .sha.label.isSigned.isVerified:hover { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified:hover { background: var(--color-green-badge-hover-bg) !important; } -.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted { border: 1px solid var(--color-yellow-badge); background: var(--color-yellow-badge-bg); } -.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover { background: var(--color-yellow-badge-hover-bg) !important; } -.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched { border: 1px solid var(--color-orange-badge); background: var(--color-orange-badge-bg); } -.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover { +.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover { background: var(--color-orange-badge-hover-bg) !important; } @@ -2339,11 +2345,107 @@ td .commit-summary { padding-top: 15px; } +.edit-label.modal .form .color.picker.column, +.new-label.modal .form .color.picker.column { + display: flex; +} + +.edit-label.modal .form .color.picker.column .minicolors, +.new-label.modal .form .color.picker.column .minicolors { + flex: 1; +} + +.edit-label.modal .form .minicolors-swatch.minicolors-sprite, +.new-label.modal .form .minicolors-swatch.minicolors-sprite { + top: 10px; + left: 10px; + width: 15px; + height: 15px; +} + +.tab-size-1 { + tab-size: 1 !important; + -moz-tab-size: 1 !important; +} + +.tab-size-2 { + tab-size: 2 !important; + -moz-tab-size: 2 !important; +} + +.tab-size-3 { + tab-size: 3 !important; + -moz-tab-size: 3 !important; +} + +.tab-size-4 { + tab-size: 4 !important; + -moz-tab-size: 4 !important; +} + +.tab-size-5 { + tab-size: 5 !important; + -moz-tab-size: 5 !important; +} + +.tab-size-6 { + tab-size: 6 !important; + -moz-tab-size: 6 !important; +} + +.tab-size-7 { + tab-size: 7 !important; + -moz-tab-size: 7 !important; +} + +.tab-size-8 { + tab-size: 8 !important; + -moz-tab-size: 8 !important; +} + +.tab-size-9 { + tab-size: 9 !important; + -moz-tab-size: 9 !important; +} + +.tab-size-10 { + tab-size: 10 !important; + -moz-tab-size: 10 !important; +} + +.tab-size-11 { + tab-size: 11 !important; + -moz-tab-size: 11 !important; +} + +.tab-size-12 { + tab-size: 12 !important; + -moz-tab-size: 12 !important; +} + +.tab-size-13 { + tab-size: 13 !important; + -moz-tab-size: 13 !important; +} + +.tab-size-14 { + tab-size: 14 !important; + -moz-tab-size: 14 !important; +} + +.tab-size-15 { + tab-size: 15 !important; + -moz-tab-size: 15 !important; +} + +.tab-size-16 { + tab-size: 16 !important; + -moz-tab-size: 16 !important; +} + .stats-table { display: table; width: 100%; - margin: 6px 0; - border-spacing: 2px; } .stats-table .table-cell { @@ -2351,34 +2453,11 @@ td .commit-summary { } .stats-table .table-cell.tiny { - height: 8px; -} - -.stats-table .table-cell:first-child { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} - -.stats-table .table-cell:last-child { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -.labels-list { - display: inline-flex; - flex-wrap: wrap; - gap: 2.5px; -} - -.labels-list a { - display: flex; - text-decoration: none; + height: 0.5em; } .labels-list .label { - padding: 0 6px; - margin: 0 !important; - min-height: 20px; + margin: 2px 0; display: inline-flex !important; line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */ } @@ -2528,7 +2607,6 @@ tbody.commit-list { #repo-topics .repo-topic { font-weight: var(--font-weight-normal); cursor: pointer; - margin: 0; } #new-dependency-drop-list.ui.selection.dropdown { @@ -2693,7 +2771,7 @@ tbody.commit-list { display: inline-block; background-color: var(--color-red); height: 12px; - width: 44px; + width: 40px; } .diff-stats-bar .diff-stats-add-bar { diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css index 609b1b3dbd..b9368df4f6 100644 --- a/web_src/css/repo/issue-card.css +++ b/web_src/css/repo/issue-card.css @@ -1,7 +1,6 @@ .issue-card { display: flex; flex-direction: column; - gap: 4px; align-items: start; border-radius: var(--border-radius); padding: 8px 10px; @@ -18,6 +17,7 @@ .issue-card-title { flex: 1; font-size: 14px; + margin-left: 4px; } .issue-card.sortable-chosen .issue-card-title { diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index 37090f71b4..d19421fcbc 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -69,6 +69,23 @@ } } +#issue-list .flex-item-title .labels-list { + display: flex; + flex-wrap: wrap; + gap: 0.25em; +} + +#issue-list .flex-item-title .labels-list a { + display: flex; + text-decoration: none; +} + +#issue-list .flex-item-title .labels-list .label { + padding: 0 6px; + margin: 0; + min-height: 20px; +} + #issue-list .flex-item-body .branches { display: inline-flex; } diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css index c74f334c2d..ed6718e40c 100644 --- a/web_src/css/themes/theme-gitea-dark.css +++ b/web_src/css/themes/theme-gitea-dark.css @@ -215,6 +215,8 @@ --color-placeholder-text: var(--color-text-light-3); --color-editor-line-highlight: var(--color-primary-light-5); --color-project-board-bg: var(--color-secondary-light-2); + --color-project-board-dark-label: #0e1011; + --color-project-board-light-label: #dde0e2; --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */ --color-reaction-bg: #e8e8ff12; --color-reaction-hover-bg: var(--color-primary-light-4); diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css index 01dd8ba4f7..b10ad7d840 100644 --- a/web_src/css/themes/theme-gitea-light.css +++ b/web_src/css/themes/theme-gitea-light.css @@ -215,6 +215,8 @@ --color-placeholder-text: var(--color-text-light-3); --color-editor-line-highlight: var(--color-primary-light-6); --color-project-board-bg: var(--color-secondary-light-4); + --color-project-board-dark-label: #0e1114; + --color-project-board-light-label: #eaeef2; --color-caret: var(--color-text-dark); --color-reaction-bg: #0000170a; --color-reaction-hover-bg: var(--color-primary-light-5); diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 49c00c4dad..525a3af8c6 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -6477,6 +6477,1727 @@ select.ui.dropdown { /******************************* Site Overrides *******************************/ +/*! + * # Fomantic-UI - Input + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +/******************************* + Standard +*******************************/ + +/*-------------------- + Inputs +---------------------*/ + +.ui.input { + position: relative; + font-weight: normal; + font-style: normal; + display: inline-flex; + color: rgba(0, 0, 0, 0.87); +} + +.ui.input > input { + margin: 0; + max-width: 100%; + flex: 1 0 auto; + outline: none; + -webkit-tap-highlight-color: rgba(255, 255, 255, 0); + text-align: left; + line-height: 1.21428571em; + font-family: var(--fonts-regular); + padding: 0.67857143em 1em; + background: #FFFFFF; + border: 1px solid rgba(34, 36, 38, 0.15); + color: rgba(0, 0, 0, 0.87); + border-radius: 0.28571429rem; + transition: box-shadow 0.1s ease, border-color 0.1s ease; + box-shadow: none; +} + +/*-------------------- + Placeholder +---------------------*/ + +/* browsers require these rules separate */ + +.ui.input > input::-webkit-input-placeholder { + color: rgba(191, 191, 191, 0.87); +} + +.ui.input > input::-moz-placeholder { + color: rgba(191, 191, 191, 0.87); +} + +.ui.input > input:-ms-input-placeholder { + color: rgba(191, 191, 191, 0.87); +} + +/******************************* + States +*******************************/ + +/*-------------------- + Disabled + ---------------------*/ + +.ui.disabled.input, +.ui.input:not(.disabled) input[disabled] { + opacity: var(--opacity-disabled); +} + +.ui.disabled.input > input, +.ui.input:not(.disabled) input[disabled] { + pointer-events: none; +} + +/*-------------------- + Active +---------------------*/ + +.ui.input > input:active, +.ui.input.down input { + border-color: rgba(0, 0, 0, 0.3); + background: #FAFAFA; + color: rgba(0, 0, 0, 0.87); + box-shadow: none; +} + +/*-------------------- + Loading + ---------------------*/ + +.ui.loading.loading.input > i.icon:before { + position: absolute; + content: ''; + top: 50%; + left: 50%; + margin: -0.64285714em 0 0 -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; + border-radius: 500rem; + border: 0.2em solid rgba(0, 0, 0, 0.1); +} + +.ui.loading.loading.input > i.icon:after { + position: absolute; + content: ''; + top: 50%; + left: 50%; + margin: -0.64285714em 0 0 -0.64285714em; + width: 1.28571429em; + height: 1.28571429em; + animation: loader 0.6s infinite linear; + border: 0.2em solid #767676; + border-radius: 500rem; + box-shadow: 0 0 0 1px transparent; +} + +/*-------------------- + Focus +---------------------*/ + +.ui.input.focus > input, +.ui.input > input:focus { + border-color: #85B7D9; + background: #FFFFFF; + color: rgba(0, 0, 0, 0.8); + box-shadow: none; +} + +.ui.input.focus > input::-webkit-input-placeholder, +.ui.input > input:focus::-webkit-input-placeholder { + color: rgba(115, 115, 115, 0.87); +} + +.ui.input.focus > input::-moz-placeholder, +.ui.input > input:focus::-moz-placeholder { + color: rgba(115, 115, 115, 0.87); +} + +.ui.input.focus > input:-ms-input-placeholder, +.ui.input > input:focus:-ms-input-placeholder { + color: rgba(115, 115, 115, 0.87); +} + +/*-------------------- + States + ---------------------*/ + +.ui.input.error > input { + background-color: #FFF6F6; + border-color: #E0B4B4; + color: #9F3A38; + box-shadow: none; +} + +/* Placeholder */ + +.ui.input.error > input::-webkit-input-placeholder { + color: #e7bdbc; +} + +.ui.input.error > input::-moz-placeholder { + color: #e7bdbc; +} + +.ui.input.error > input:-ms-input-placeholder { + color: #e7bdbc !important; +} + +/* Focused Placeholder */ + +.ui.input.error > input:focus::-webkit-input-placeholder { + color: #da9796; +} + +.ui.input.error > input:focus::-moz-placeholder { + color: #da9796; +} + +.ui.input.error > input:focus:-ms-input-placeholder { + color: #da9796 !important; +} + +.ui.input.info > input { + background-color: #F8FFFF; + border-color: #A9D5DE; + color: #276F86; + box-shadow: none; +} + +/* Placeholder */ + +.ui.input.info > input::-webkit-input-placeholder { + color: #98cfe1; +} + +.ui.input.info > input::-moz-placeholder { + color: #98cfe1; +} + +.ui.input.info > input:-ms-input-placeholder { + color: #98cfe1 !important; +} + +/* Focused Placeholder */ + +.ui.input.info > input:focus::-webkit-input-placeholder { + color: #70bdd6; +} + +.ui.input.info > input:focus::-moz-placeholder { + color: #70bdd6; +} + +.ui.input.info > input:focus:-ms-input-placeholder { + color: #70bdd6 !important; +} + +.ui.input.success > input { + background-color: #FCFFF5; + border-color: #A3C293; + color: #2C662D; + box-shadow: none; +} + +/* Placeholder */ + +.ui.input.success > input::-webkit-input-placeholder { + color: #8fcf90; +} + +.ui.input.success > input::-moz-placeholder { + color: #8fcf90; +} + +.ui.input.success > input:-ms-input-placeholder { + color: #8fcf90 !important; +} + +/* Focused Placeholder */ + +.ui.input.success > input:focus::-webkit-input-placeholder { + color: #6cbf6d; +} + +.ui.input.success > input:focus::-moz-placeholder { + color: #6cbf6d; +} + +.ui.input.success > input:focus:-ms-input-placeholder { + color: #6cbf6d !important; +} + +.ui.input.warning > input { + background-color: #FFFAF3; + border-color: #C9BA9B; + color: #573A08; + box-shadow: none; +} + +/* Placeholder */ + +.ui.input.warning > input::-webkit-input-placeholder { + color: #edad3e; +} + +.ui.input.warning > input::-moz-placeholder { + color: #edad3e; +} + +.ui.input.warning > input:-ms-input-placeholder { + color: #edad3e !important; +} + +/* Focused Placeholder */ + +.ui.input.warning > input:focus::-webkit-input-placeholder { + color: #e39715; +} + +.ui.input.warning > input:focus::-moz-placeholder { + color: #e39715; +} + +.ui.input.warning > input:focus:-ms-input-placeholder { + color: #e39715 !important; +} + +/******************************* + Variations +*******************************/ + +/*-------------------- + Transparent + ---------------------*/ + +.ui.transparent.input > textarea, +.ui.transparent.input > input { + border-color: transparent !important; + background-color: transparent !important; + padding: 0; + box-shadow: none !important; + border-radius: 0 !important; +} + +.field .ui.transparent.input > textarea { + padding: 0.67857143em 1em; +} + +/* Transparent Icon */ + +:not(.field) > .ui.transparent.icon.input > i.icon { + width: 1.1em; +} + +:not(.field) > .ui.ui.ui.transparent.icon.input > input { + padding-left: 0; + padding-right: 2em; +} + +:not(.field) > .ui.ui.ui.transparent[class*="left icon"].input > input { + padding-left: 2em; + padding-right: 0; +} + +/*-------------------- + Icon + ---------------------*/ + +.ui.icon.input > i.icon { + cursor: default; + position: absolute; + line-height: 1; + text-align: center; + top: 0; + right: 0; + margin: 0; + height: 100%; + width: 2.67142857em; + opacity: 0.5; + border-radius: 0 0.28571429rem 0.28571429rem 0; + transition: opacity 0.3s ease; +} + +.ui.icon.input > i.icon:not(.link) { + pointer-events: none; +} + +.ui.ui.ui.ui.icon.input > textarea, +.ui.ui.ui.ui.icon.input > input { + padding-right: 2.67142857em; +} + +.ui.icon.input > i.icon:before, +.ui.icon.input > i.icon:after { + left: 0; + position: absolute; + text-align: center; + top: 50%; + width: 100%; + margin-top: -0.5em; +} + +.ui.icon.input > i.link.icon { + cursor: pointer; +} + +.ui.icon.input > i.circular.icon { + top: 0.35em; + right: 0.5em; +} + +/* Left Icon Input */ + +.ui[class*="left icon"].input > i.icon { + right: auto; + left: 1px; + border-radius: 0.28571429rem 0 0 0.28571429rem; +} + +.ui[class*="left icon"].input > i.circular.icon { + right: auto; + left: 0.5em; +} + +.ui.ui.ui.ui[class*="left icon"].input > textarea, +.ui.ui.ui.ui[class*="left icon"].input > input { + padding-left: 2.67142857em; + padding-right: 1em; +} + +/* Focus */ + +.ui.icon.input > textarea:focus ~ i.icon, +.ui.icon.input > input:focus ~ i.icon { + opacity: 1; +} + +/*-------------------- + Labeled + ---------------------*/ + +/* Adjacent Label */ + +.ui.labeled.input > .label { + flex: 0 0 auto; + margin: 0; + font-size: 1em; +} + +.ui.labeled.input > .label:not(.corner) { + padding-top: 0.78571429em; + padding-bottom: 0.78571429em; +} + +/* Regular Label on Left */ + +.ui.labeled.input:not([class*="corner labeled"]) .label:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.ui.labeled.input:not([class*="corner labeled"]) .label:first-child + input { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left-color: transparent; +} + +.ui.labeled.input:not([class*="corner labeled"]) .label:first-child + input:focus { + border-left-color: #85B7D9; +} + +/* Regular Label on Right */ + +.ui[class*="right labeled"].input > input { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-right-color: transparent !important; +} + +.ui[class*="right labeled"].input > input + .label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.ui[class*="right labeled"].input > input:focus { + border-right-color: #85B7D9 !important; +} + +/* Corner Label */ + +.ui.labeled.input .corner.label { + top: 1px; + right: 1px; + font-size: 0.64285714em; + border-radius: 0 0.28571429rem 0 0; +} + +/* Spacing with corner label */ + +.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input > textarea, +.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input > input { + padding-right: 2.5em !important; +} + +.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > textarea, +.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > input { + padding-right: 3.25em !important; +} + +.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"]) > i.icon { + margin-right: 1.25em; +} + +/* Left Labeled */ + +.ui[class*="left corner labeled"].labeled.input > textarea, +.ui[class*="left corner labeled"].labeled.input > input { + padding-left: 2.5em !important; +} + +.ui[class*="left corner labeled"].icon.input > textarea, +.ui[class*="left corner labeled"].icon.input > input { + padding-left: 3.25em !important; +} + +.ui[class*="left corner labeled"].icon.input > i.icon { + margin-left: 1.25em; +} + +.ui.icon.input > textarea ~ i.icon { + height: 3em; +} + +:not(.field) > .ui.transparent.icon.input > textarea ~ i.icon { + height: 1.3em; +} + +/* Corner Label Position */ + +.ui.input > .ui.corner.label { + top: 1px; + right: 1px; +} + +.ui.input > .ui.left.corner.label { + right: auto; + left: 1px; +} + +/* Labeled and action input states */ + +.ui.form .field.error > .ui.action.input > .ui.button, +.ui.form .field.error > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label, +.ui.action.input.error > .ui.button, +.ui.labeled.input.error:not([class*="corner labeled"]) > .ui.label { + border-top: 1px solid #E0B4B4; + border-bottom: 1px solid #E0B4B4; +} + +.ui.form .field.error > .ui[class*="left action"].input > .ui.button, +.ui.form .field.error > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label, +.ui[class*="left action"].input.error > .ui.button, +.ui.labeled.input.error:not(.right):not([class*="corner labeled"]) > .ui.label { + border-left: 1px solid #E0B4B4; +} + +.ui.form .field.error > .ui.action.input:not([class*="left action"]) > input + .ui.button, +.ui.form .field.error > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label, +.ui.action.input.error:not([class*="left action"]) > input + .ui.button, +.ui.right.labeled.input.error:not([class*="corner labeled"]) > input + .ui.label { + border-right: 1px solid #E0B4B4; +} + +.ui.form .field.error > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child, +.ui.right.labeled.input.error:not([class*="corner labeled"]) > .ui.label:first-child { + border-left: 1px solid #E0B4B4; +} + +.ui.form .field.info > .ui.action.input > .ui.button, +.ui.form .field.info > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label, +.ui.action.input.info > .ui.button, +.ui.labeled.input.info:not([class*="corner labeled"]) > .ui.label { + border-top: 1px solid #A9D5DE; + border-bottom: 1px solid #A9D5DE; +} + +.ui.form .field.info > .ui[class*="left action"].input > .ui.button, +.ui.form .field.info > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label, +.ui[class*="left action"].input.info > .ui.button, +.ui.labeled.input.info:not(.right):not([class*="corner labeled"]) > .ui.label { + border-left: 1px solid #A9D5DE; +} + +.ui.form .field.info > .ui.action.input:not([class*="left action"]) > input + .ui.button, +.ui.form .field.info > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label, +.ui.action.input.info:not([class*="left action"]) > input + .ui.button, +.ui.right.labeled.input.info:not([class*="corner labeled"]) > input + .ui.label { + border-right: 1px solid #A9D5DE; +} + +.ui.form .field.info > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child, +.ui.right.labeled.input.info:not([class*="corner labeled"]) > .ui.label:first-child { + border-left: 1px solid #A9D5DE; +} + +.ui.form .field.success > .ui.action.input > .ui.button, +.ui.form .field.success > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label, +.ui.action.input.success > .ui.button, +.ui.labeled.input.success:not([class*="corner labeled"]) > .ui.label { + border-top: 1px solid #A3C293; + border-bottom: 1px solid #A3C293; +} + +.ui.form .field.success > .ui[class*="left action"].input > .ui.button, +.ui.form .field.success > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label, +.ui[class*="left action"].input.success > .ui.button, +.ui.labeled.input.success:not(.right):not([class*="corner labeled"]) > .ui.label { + border-left: 1px solid #A3C293; +} + +.ui.form .field.success > .ui.action.input:not([class*="left action"]) > input + .ui.button, +.ui.form .field.success > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label, +.ui.action.input.success:not([class*="left action"]) > input + .ui.button, +.ui.right.labeled.input.success:not([class*="corner labeled"]) > input + .ui.label { + border-right: 1px solid #A3C293; +} + +.ui.form .field.success > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child, +.ui.right.labeled.input.success:not([class*="corner labeled"]) > .ui.label:first-child { + border-left: 1px solid #A3C293; +} + +.ui.form .field.warning > .ui.action.input > .ui.button, +.ui.form .field.warning > .ui.labeled.input:not([class*="corner labeled"]) > .ui.label, +.ui.action.input.warning > .ui.button, +.ui.labeled.input.warning:not([class*="corner labeled"]) > .ui.label { + border-top: 1px solid #C9BA9B; + border-bottom: 1px solid #C9BA9B; +} + +.ui.form .field.warning > .ui[class*="left action"].input > .ui.button, +.ui.form .field.warning > .ui.labeled.input:not(.right):not([class*="corner labeled"]) > .ui.label, +.ui[class*="left action"].input.warning > .ui.button, +.ui.labeled.input.warning:not(.right):not([class*="corner labeled"]) > .ui.label { + border-left: 1px solid #C9BA9B; +} + +.ui.form .field.warning > .ui.action.input:not([class*="left action"]) > input + .ui.button, +.ui.form .field.warning > .ui.right.labeled.input:not([class*="corner labeled"]) > input + .ui.label, +.ui.action.input.warning:not([class*="left action"]) > input + .ui.button, +.ui.right.labeled.input.warning:not([class*="corner labeled"]) > input + .ui.label { + border-right: 1px solid #C9BA9B; +} + +.ui.form .field.warning > .ui.right.labeled.input:not([class*="corner labeled"]) > .ui.label:first-child, +.ui.right.labeled.input.warning:not([class*="corner labeled"]) > .ui.label:first-child { + border-left: 1px solid #C9BA9B; +} + +/*-------------------- + Action + ---------------------*/ + +.ui.action.input > .button, +.ui.action.input > .buttons { + display: flex; + align-items: center; + flex: 0 0 auto; +} + +.ui.action.input > .button, +.ui.action.input > .buttons > .button { + padding-top: 0.78571429em; + padding-bottom: 0.78571429em; + margin: 0; +} + +/* Input when ui Left*/ + +.ui[class*="left action"].input > input { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left-color: transparent; +} + +/* Input when ui Right*/ + +.ui.action.input:not([class*="left action"]) > input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right-color: transparent; +} + +/* Button and Dropdown */ + +.ui.action.input > .dropdown:first-child, +.ui.action.input > .button:first-child, +.ui.action.input > .buttons:first-child > .button { + border-radius: 0.28571429rem 0 0 0.28571429rem; +} + +.ui.action.input > .dropdown:not(:first-child), +.ui.action.input > .button:not(:first-child), +.ui.action.input > .buttons:not(:first-child) > .button { + border-radius: 0; +} + +.ui.action.input > .dropdown:last-child, +.ui.action.input > .button:last-child, +.ui.action.input > .buttons:last-child > .button { + border-radius: 0 0.28571429rem 0.28571429rem 0; +} + +/* Input Focus */ + +.ui.action.input:not([class*="left action"]) > input:focus { + border-right-color: #85B7D9; +} + +.ui.ui[class*="left action"].input > input:focus { + border-left-color: #85B7D9; +} + +/*-------------------- + Fluid + ---------------------*/ + +.ui.fluid.input { + display: flex; +} + +.ui.fluid.input > input { + width: 0 !important; +} + +/*-------------------- + Size +---------------------*/ + +.ui.input { + font-size: 1em; +} + +.ui.mini.input { + font-size: 0.78571429em; +} + +.ui.tiny.input { + font-size: 0.85714286em; +} + +.ui.small.input { + font-size: 0.92857143em; +} + +.ui.large.input { + font-size: 1.14285714em; +} + +.ui.big.input { + font-size: 1.28571429em; +} + +.ui.huge.input { + font-size: 1.42857143em; +} + +.ui.massive.input { + font-size: 1.71428571em; +} + +/******************************* + Theme Overrides +*******************************/ + +/******************************* + Site Overrides +*******************************/ +/*! + * # Fomantic-UI - List + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +/******************************* + List +*******************************/ + +ul.ui.list, +ol.ui.list, +.ui.list { + list-style-type: none; + margin: 1em 0; + padding: 0 0; +} + +ul.ui.list:first-child, +ol.ui.list:first-child, +.ui.list:first-child { + margin-top: 0; + padding-top: 0; +} + +ul.ui.list:last-child, +ol.ui.list:last-child, +.ui.list:last-child { + margin-bottom: 0; + padding-bottom: 0; +} + +/******************************* + Content +*******************************/ + +/* List Item */ + +ul.ui.list li, +ol.ui.list li, +.ui.list > .item, +.ui.list .list > .item { + display: list-item; + table-layout: fixed; + list-style-type: none; + list-style-position: outside; + padding: 0.21428571em 0; + line-height: 1.14285714em; +} + +ul.ui.list > li:first-child:after, +ol.ui.list > li:first-child:after, +.ui.list > .list > .item:after, +.ui.list > .item:after { + content: ''; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +ul.ui.list li:first-child, +ol.ui.list li:first-child, +.ui.list .list > .item:first-child, +.ui.list > .item:first-child { + padding-top: 0; +} + +ul.ui.list li:last-child, +ol.ui.list li:last-child, +.ui.list .list > .item:last-child, +.ui.list > .item:last-child { + padding-bottom: 0; +} + +/* Child List */ + +ul.ui.list ul, +ol.ui.list ol, +.ui.list .list:not(.icon) { + clear: both; + margin: 0; + padding: 0.75em 0 0.25em 0.5em; +} + +/* Child Item */ + +ul.ui.list ul li, +ol.ui.list ol li, +.ui.list .list > .item { + padding: 0.14285714em 0; + line-height: inherit; +} + +/* Icon */ + +.ui.list .list > .item > i.icon, +.ui.list > .item > i.icon { + display: table-cell; + min-width: 1.55em; + margin: 0; + padding-top: 0; + transition: color 0.1s ease; +} + +.ui.list .list > .item > i.icon:not(.loading), +.ui.list > .item > i.icon:not(.loading) { + padding-right: 0.28571429em; + vertical-align: top; +} + +.ui.list .list > .item > i.icon:only-child, +.ui.list > .item > i.icon:only-child { + display: inline-block; + min-width: auto; + vertical-align: top; +} + +/* Image */ + +.ui.list .list > .item > .image, +.ui.list > .item > .image { + display: table-cell; + background-color: transparent; + margin: 0; + vertical-align: top; +} + +.ui.list .list > .item > .image:not(:only-child):not(img), +.ui.list > .item > .image:not(:only-child):not(img) { + padding-right: 0.5em; +} + +.ui.list .list > .item > .image img, +.ui.list > .item > .image img { + vertical-align: top; +} + +.ui.list .list > .item > img.image, +.ui.list .list > .item > .image:only-child, +.ui.list > .item > img.image, +.ui.list > .item > .image:only-child { + display: inline-block; +} + +/* Content */ + +.ui.list .list > .item > .content, +.ui.list > .item > .content { + line-height: 1.14285714em; + color: rgba(0, 0, 0, 0.87); +} + +.ui.list .list > .item > .image + .content, +.ui.list .list > .item > i.icon + .content, +.ui.list > .item > .image + .content, +.ui.list > .item > i.icon + .content { + display: table-cell; + width: 100%; + padding: 0 0 0 0.5em; + vertical-align: top; +} + +.ui.list .list > .item > i.loading.icon + .content, +.ui.list > .item > i.loading.icon + .content { + padding-left: calc(0.2857142857142857em + 0.5em); +} + +.ui.list .list > .item > img.image + .content, +.ui.list > .item > img.image + .content { + display: inline-block; + width: auto; +} + +.ui.list .list > .item > .content > .list, +.ui.list > .item > .content > .list { + margin-left: 0; + padding-left: 0; +} + +/* Header */ + +.ui.list .list > .item .header, +.ui.list > .item .header { + display: block; + margin: 0; + font-family: var(--fonts-regular); + font-weight: 500; + color: rgba(0, 0, 0, 0.87); +} + +/* Description */ + +.ui.list .list > .item .description, +.ui.list > .item .description { + display: block; + color: rgba(0, 0, 0, 0.7); +} + +/* Child Link */ + +.ui.list > .item a, +.ui.list .list > .item a { + cursor: pointer; +} + +/* Linking Item */ + +.ui.list .list > a.item, +.ui.list > a.item { + cursor: pointer; + color: #4183C4; +} + +.ui.list .list > a.item:hover, +.ui.list > a.item:hover { + color: #1e70bf; +} + +/* Linked Item Icons */ + +.ui.list .list > a.item > i.icons, +.ui.list > a.item > i.icons, +.ui.list .list > a.item > i.icon, +.ui.list > a.item > i.icon { + color: rgba(0, 0, 0, 0.4); +} + +/* Header Link */ + +.ui.list .list > .item a.header, +.ui.list > .item a.header { + cursor: pointer; + color: #4183C4 !important; +} + +.ui.list .list > .item > a.header:hover, +.ui.list > .item > a.header:hover { + color: #1e70bf !important; +} + +/* Floated Content */ + +.ui[class*="left floated"].list { + float: left; +} + +.ui[class*="right floated"].list { + float: right; +} + +.ui.list .list > .item [class*="left floated"], +.ui.list > .item [class*="left floated"] { + float: left; + margin: 0 1em 0 0; +} + +.ui.list .list > .item [class*="right floated"], +.ui.list > .item [class*="right floated"] { + float: right; + margin: 0 0 0 1em; +} + +/******************************* + Coupling +*******************************/ + +.ui.menu .ui.list > .item, +.ui.menu .ui.list .list > .item { + display: list-item; + table-layout: fixed; + background-color: transparent; + list-style-type: none; + list-style-position: outside; + padding: 0.21428571em 0; + line-height: 1.14285714em; +} + +.ui.menu .ui.list .list > .item:before, +.ui.menu .ui.list > .item:before { + border: none; + background: none; +} + +.ui.menu .ui.list .list > .item:first-child, +.ui.menu .ui.list > .item:first-child { + padding-top: 0; +} + +.ui.menu .ui.list .list > .item:last-child, +.ui.menu .ui.list > .item:last-child { + padding-bottom: 0; +} + +/******************************* + Types +*******************************/ + +/*------------------- + Horizontal + --------------------*/ + +.ui.horizontal.list { + display: inline-block; + font-size: 0; +} + +.ui.horizontal.list > .item { + display: inline-block; + margin-right: 1em; + font-size: 1rem; +} + +.ui.horizontal.list:not(.celled) > .item:last-child { + margin-right: 0; + padding-right: 0; +} + +.ui.horizontal.list .list:not(.icon) { + padding-left: 0; + padding-bottom: 0; +} + +.ui.horizontal.list > .item > .image, +.ui.horizontal.list .list > .item > .image, +.ui.horizontal.list > .item > i.icon, +.ui.horizontal.list .list > .item > i.icon, +.ui.horizontal.list > .item > .content, +.ui.horizontal.list .list > .item > .content { + vertical-align: middle; +} + +/* Padding on all elements */ + +.ui.horizontal.list > .item:first-child, +.ui.horizontal.list > .item:last-child { + padding-top: 0.21428571em; + padding-bottom: 0.21428571em; +} + +/* Horizontal List */ + +.ui.horizontal.list > .item > i.icon, +.ui.horizontal.list .item > i.icons > i.icon { + margin: 0; + padding: 0 0.25em 0 0; +} + +.ui.horizontal.list > .item > .image + .content, +.ui.horizontal.list > .item > i.icon, +.ui.horizontal.list > .item > i.icon + .content { + float: none; + display: inline-block; + width: auto; +} + +.ui.horizontal.list > .item > .image { + display: inline-block; +} + +/******************************* + States +*******************************/ + +/*------------------- + Disabled + --------------------*/ + +.ui.list .list > .disabled.item, +.ui.list > .disabled.item { + pointer-events: none; + color: rgba(40, 40, 40, 0.3) !important; +} + +/*------------------- + Hover +--------------------*/ + +.ui.list .list > a.item:hover > .icons, +.ui.list > a.item:hover > .icons, +.ui.list .list > a.item:hover > i.icon, +.ui.list > a.item:hover > i.icon { + color: rgba(0, 0, 0, 0.87); +} + +/******************************* + Variations +*******************************/ + +/*------------------- + Aligned + --------------------*/ + +.ui.list[class*="top aligned"] .image, +.ui.list[class*="top aligned"] .content, +.ui.list [class*="top aligned"] { + vertical-align: top !important; +} + +.ui.list[class*="middle aligned"] .image, +.ui.list[class*="middle aligned"] .content, +.ui.list [class*="middle aligned"] { + vertical-align: middle !important; +} + +.ui.list[class*="bottom aligned"] .image, +.ui.list[class*="bottom aligned"] .content, +.ui.list [class*="bottom aligned"] { + vertical-align: bottom !important; +} + +/*------------------- + Link + --------------------*/ + +.ui.link.list .item, +.ui.link.list a.item, +.ui.link.list .item a:not(.ui) { + color: rgba(0, 0, 0, 0.4); + transition: 0.1s color ease; +} + +.ui.link.list.list a.item:hover, +.ui.link.list.list .item a:not(.ui):hover { + color: rgba(0, 0, 0, 0.8); +} + +.ui.link.list.list a.item:active, +.ui.link.list.list .item a:not(.ui):active { + color: rgba(0, 0, 0, 0.9); +} + +.ui.link.list.list .active.item, +.ui.link.list.list .active.item a:not(.ui) { + color: rgba(0, 0, 0, 0.95); +} + +/*------------------- + Selection + --------------------*/ + +.ui.selection.list .list > .item, +.ui.selection.list > .item { + cursor: pointer; + background: transparent; + padding: 0.5em 0.5em; + margin: 0; + color: rgba(0, 0, 0, 0.4); + border-radius: 0.5em; + transition: 0.1s color ease, 0.1s padding-left ease, 0.1s background-color ease; +} + +.ui.selection.list .list > .item:last-child, +.ui.selection.list > .item:last-child { + margin-bottom: 0; +} + +.ui.selection.list .list > .item:hover, +.ui.selection.list > .item:hover { + background: rgba(0, 0, 0, 0.03); + color: rgba(0, 0, 0, 0.8); +} + +.ui.selection.list .list > .item:active, +.ui.selection.list > .item:active { + background: rgba(0, 0, 0, 0.05); + color: rgba(0, 0, 0, 0.9); +} + +.ui.selection.list .list > .item.active, +.ui.selection.list > .item.active { + background: rgba(0, 0, 0, 0.05); + color: rgba(0, 0, 0, 0.95); +} + +/* Celled / Divided Selection List */ + +.ui.celled.selection.list .list > .item, +.ui.divided.selection.list .list > .item, +.ui.celled.selection.list > .item, +.ui.divided.selection.list > .item { + border-radius: 0; +} + +/*------------------- + Animated + --------------------*/ + +.ui.animated.list > .item { + transition: 0.25s color ease 0.1s, 0.25s padding-left ease 0.1s, 0.25s background-color ease 0.1s; +} + +.ui.animated.list:not(.horizontal) > .item:hover { + padding-left: 1em; +} + +/*------------------- + Fitted + --------------------*/ + +.ui.fitted.list:not(.selection) .list > .item, +.ui.fitted.list:not(.selection) > .item { + padding-left: 0; + padding-right: 0; +} + +.ui.fitted.selection.list .list > .item, +.ui.fitted.selection.list > .item { + margin-left: -0.5em; + margin-right: -0.5em; +} + +/*------------------- + Bulleted + --------------------*/ + +ul.ui.list, +.ui.bulleted.list { + margin-left: 1.25rem; +} + +ul.ui.list li, +.ui.bulleted.list .list > .item, +.ui.bulleted.list > .item { + position: relative; +} + +ul.ui.list li:before, +.ui.bulleted.list .list > .item:before, +.ui.bulleted.list > .item:before { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + pointer-events: none; + position: absolute; + top: auto; + left: auto; + font-weight: normal; + margin-left: -1.25rem; + content: '\2022'; + opacity: 1; + color: inherit; + vertical-align: top; +} + +ul.ui.list li:before, +.ui.bulleted.list .list > a.item:before, +.ui.bulleted.list > a.item:before { + color: rgba(0, 0, 0, 0.87); +} + +ul.ui.list ul, +.ui.bulleted.list .list:not(.icon) { + padding-left: 1.25rem; +} + +/* Horizontal Bulleted */ + +ul.ui.horizontal.bulleted.list, +.ui.horizontal.bulleted.list { + margin-left: 0; +} + +ul.ui.horizontal.bulleted.list li, +.ui.horizontal.bulleted.list > .item { + margin-left: 1.75rem; +} + +ul.ui.horizontal.bulleted.list li:first-child, +.ui.horizontal.bulleted.list > .item:first-child { + margin-left: 0; +} + +ul.ui.horizontal.bulleted.list li::before, +.ui.horizontal.bulleted.list > .item::before { + color: rgba(0, 0, 0, 0.87); +} + +ul.ui.horizontal.bulleted.list li:first-child::before, +.ui.horizontal.bulleted.list > .item:first-child::before { + display: none; +} + +/*------------------- + Ordered + --------------------*/ + +ol.ui.list, +.ui.ordered.list, +.ui.ordered.list .list:not(.icon), +ol.ui.list ol { + counter-reset: ordered; + margin-left: 1.25rem; + list-style-type: none; +} + +ol.ui.list li, +.ui.ordered.list .list > .item, +.ui.ordered.list > .item { + list-style-type: none; + position: relative; +} + +ol.ui.list li:before, +.ui.ordered.list .list > .item:before, +.ui.ordered.list > .item:before { + position: absolute; + top: auto; + left: auto; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + pointer-events: none; + margin-left: -1.25rem; + counter-increment: ordered; + content: counters(ordered, ".") " "; + text-align: right; + color: rgba(0, 0, 0, 0.87); + vertical-align: middle; + opacity: 0.8; +} + +/* Value */ + +.ui.ordered.list .list > .item[data-value]:before, +.ui.ordered.list > .item[data-value]:before { + content: attr(data-value); +} + +ol.ui.list li[value]:before { + content: attr(value); +} + +/* Child Lists */ + +ol.ui.list ol, +.ui.ordered.list .list:not(.icon) { + margin-left: 1em; +} + +ol.ui.list ol li:before, +.ui.ordered.list .list > .item:before { + margin-left: -2em; +} + +/* Horizontal Ordered */ + +ol.ui.horizontal.list, +.ui.ordered.horizontal.list { + margin-left: 0; +} + +ol.ui.horizontal.list li:before, +.ui.ordered.horizontal.list .list > .item:before, +.ui.ordered.horizontal.list > .item:before { + position: static; + margin: 0 0.5em 0 0; +} + +/* Suffixed Ordered */ + +ol.ui.suffixed.list li:before, +.ui.suffixed.ordered.list .list > .item:before, +.ui.suffixed.ordered.list > .item:before { + content: counters(ordered, ".") "."; +} + +/*------------------- + Divided + --------------------*/ + +.ui.divided.list > .item { + border-top: 1px solid rgba(34, 36, 38, 0.15); +} + +.ui.divided.list .list > .item { + border-top: none; +} + +.ui.divided.list .item .list > .item { + border-top: none; +} + +.ui.divided.list .list > .item:first-child, +.ui.divided.list > .item:first-child { + border-top: none; +} + +/* Sub Menu */ + +.ui.divided.list:not(.horizontal) .list > .item:first-child { + border-top-width: 1px; +} + +/* Divided bulleted */ + +.ui.divided.bulleted.list:not(.horizontal), +.ui.divided.bulleted.list .list:not(.icon) { + margin-left: 0; + padding-left: 0; +} + +.ui.divided.bulleted.list > .item:not(.horizontal) { + padding-left: 1.25rem; +} + +/* Divided Ordered */ + +.ui.divided.ordered.list { + margin-left: 0; +} + +.ui.divided.ordered.list .list > .item, +.ui.divided.ordered.list > .item { + padding-left: 1.25rem; +} + +.ui.divided.ordered.list .item .list:not(.icon) { + margin-left: 0; + margin-right: 0; + padding-bottom: 0.21428571em; +} + +.ui.divided.ordered.list .item .list > .item { + padding-left: 1em; +} + +/* Divided Selection */ + +.ui.divided.selection.list .list > .item, +.ui.divided.selection.list > .item { + margin: 0; + border-radius: 0; +} + +/* Divided horizontal */ + +.ui.divided.horizontal.list { + margin-left: 0; +} + +.ui.divided.horizontal.list > .item { + padding-left: 0.5em; +} + +.ui.divided.horizontal.list > .item:not(:last-child) { + padding-right: 0.5em; +} + +.ui.divided.horizontal.list > .item { + border-top: none; + border-right: 1px solid rgba(34, 36, 38, 0.15); + margin: 0; + line-height: 0.6; +} + +.ui.horizontal.divided.list > .item:last-child { + border-right: none; +} + +/*------------------- + Celled + --------------------*/ + +.ui.celled.list > .item, +.ui.celled.list > .list { + border-top: 1px solid rgba(34, 36, 38, 0.15); + padding-left: 0.5em; + padding-right: 0.5em; +} + +.ui.celled.list > .item:last-child { + border-bottom: 1px solid rgba(34, 36, 38, 0.15); +} + +/* Padding on all elements */ + +.ui.celled.list > .item:first-child, +.ui.celled.list > .item:last-child { + padding-top: 0.21428571em; + padding-bottom: 0.21428571em; +} + +/* Sub Menu */ + +.ui.celled.list .item .list > .item { + border-width: 0; +} + +.ui.celled.list .list > .item:first-child { + border-top-width: 0; +} + +/* Celled Bulleted */ + +.ui.celled.bulleted.list { + margin-left: 0; +} + +.ui.celled.bulleted.list .list > .item, +.ui.celled.bulleted.list > .item { + padding-left: 1.25rem; +} + +.ui.celled.bulleted.list .item .list:not(.icon) { + margin-left: -1.25rem; + margin-right: -1.25rem; + padding-bottom: 0.21428571em; +} + +/* Celled Ordered */ + +.ui.celled.ordered.list { + margin-left: 0; +} + +.ui.celled.ordered.list .list > .item, +.ui.celled.ordered.list > .item { + padding-left: 1.25rem; +} + +.ui.celled.ordered.list .item .list:not(.icon) { + margin-left: 0; + margin-right: 0; + padding-bottom: 0.21428571em; +} + +.ui.celled.ordered.list .list > .item { + padding-left: 1em; +} + +/* Celled Horizontal */ + +.ui.horizontal.celled.list { + margin-left: 0; +} + +.ui.horizontal.celled.list .list > .item, +.ui.horizontal.celled.list > .item { + border-top: none; + border-left: 1px solid rgba(34, 36, 38, 0.15); + margin: 0; + padding-left: 0.5em; + padding-right: 0.5em; + line-height: 0.6; +} + +.ui.horizontal.celled.list .list > .item:last-child, +.ui.horizontal.celled.list > .item:last-child { + border-bottom: none; + border-right: 1px solid rgba(34, 36, 38, 0.15); +} + +/*------------------- + Relaxed + --------------------*/ + +.ui.relaxed.list:not(.horizontal) > .item:not(:first-child) { + padding-top: 0.42857143em; +} + +.ui.relaxed.list:not(.horizontal) > .item:not(:last-child) { + padding-bottom: 0.42857143em; +} + +.ui.horizontal.relaxed.list .list > .item:not(:first-child), +.ui.horizontal.relaxed.list > .item:not(:first-child) { + padding-left: 1rem; +} + +.ui.horizontal.relaxed.list .list > .item:not(:last-child), +.ui.horizontal.relaxed.list > .item:not(:last-child) { + padding-right: 1rem; +} + +/* Very Relaxed */ + +.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:first-child) { + padding-top: 0.85714286em; +} + +.ui[class*="very relaxed"].list:not(.horizontal) > .item:not(:last-child) { + padding-bottom: 0.85714286em; +} + +.ui.horizontal[class*="very relaxed"].list .list > .item:not(:first-child), +.ui.horizontal[class*="very relaxed"].list > .item:not(:first-child) { + padding-left: 1.5rem; +} + +.ui.horizontal[class*="very relaxed"].list .list > .item:not(:last-child), +.ui.horizontal[class*="very relaxed"].list > .item:not(:last-child) { + padding-right: 1.5rem; +} + +/*------------------- + Sizes +--------------------*/ + +.ui.list { + font-size: 1em; +} + +.ui.mini.list { + font-size: 0.78571429em; +} + +.ui.mini.horizontal.list .list > .item, +.ui.mini.horizontal.list > .item { + font-size: 0.78571429rem; +} + +.ui.tiny.list { + font-size: 0.85714286em; +} + +.ui.tiny.horizontal.list .list > .item, +.ui.tiny.horizontal.list > .item { + font-size: 0.85714286rem; +} + +.ui.small.list { + font-size: 0.92857143em; +} + +.ui.small.horizontal.list .list > .item, +.ui.small.horizontal.list > .item { + font-size: 0.92857143rem; +} + +.ui.large.list { + font-size: 1.14285714em; +} + +.ui.large.horizontal.list .list > .item, +.ui.large.horizontal.list > .item { + font-size: 1.14285714rem; +} + +.ui.big.list { + font-size: 1.28571429em; +} + +.ui.big.horizontal.list .list > .item, +.ui.big.horizontal.list > .item { + font-size: 1.28571429rem; +} + +.ui.huge.list { + font-size: 1.42857143em; +} + +.ui.huge.horizontal.list .list > .item, +.ui.huge.horizontal.list > .item { + font-size: 1.42857143rem; +} + +.ui.massive.list { + font-size: 1.71428571em; +} + +.ui.massive.horizontal.list .list > .item, +.ui.massive.horizontal.list > .item { + font-size: 1.71428571rem; +} + +/******************************* + Theme Overrides +*******************************/ + +/******************************* + User Variable Overrides +*******************************/ /* * # Fomantic - Menu * http://github.com/fomantic/Fomantic-UI/ diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 5db57bc8d4..151273f3ca 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -26,6 +26,8 @@ "dimmer", "dropdown", "form", + "input", + "list", "menu", "modal", "search", diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index 70b12dcb28..ac6a8f3bb6 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,6 +1,7 @@