0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-14 10:57:54 +02:00

Merge remote-tracking branch 'upstream/main' into ci-phase2-push

# Conflicts:
#	templates/base/head_navbar_icons.tmpl
This commit is contained in:
Epid 2026-04-02 11:33:25 +03:00
commit 0af095a419
162 changed files with 5081 additions and 2632 deletions

View File

@ -198,10 +198,11 @@ Here's how to run the test suite:
- E2E test environment variables
| Variable | Description |
| :------------------------ | :---------------------------------------------------------------- |
| ``GITEA_TEST_E2E_DEBUG`` | When set, show Gitea server output |
| ``GITEA_TEST_E2E_FLAGS`` | Additional flags passed to Playwright, for example ``--ui`` |
| Variable | Description |
| :-------------------------------- | :---------------------------------------------------------- |
| ``GITEA_TEST_E2E_DEBUG`` | When set, show Gitea server output |
| ``GITEA_TEST_E2E_FLAGS`` | Additional flags passed to Playwright, for example ``--ui`` |
| ``GITEA_TEST_E2E_TIMEOUT_FACTOR`` | Timeout multiplier (default: 3 on CI, 1 locally) |
## Translation

View File

@ -15,7 +15,7 @@ XGO_VERSION := go-1.25.x
AIR_PACKAGE ?= github.com/air-verse/air@v1
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.2
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1
@ -81,15 +81,6 @@ STORED_VERSION_FILE := VERSION
GITHUB_REF_TYPE ?= branch
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
# Enable typescript support in Node.js before 22.18
# TODO: Remove this once we can raise the minimum Node.js version to 22.18 (alpine >= 3.23)
NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v 2>/dev/null | cut -c2- | sed 's/-.*//' | tr '.' ' '))
ifeq ($(shell test "$(NODE_VERSION)" -lt "022018000"; echo $$?),0)
NODE_VARS := NODE_OPTIONS="--experimental-strip-types"
else
NODE_VARS :=
endif
ifneq ($(GITHUB_REF_TYPE),branch)
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
GITEA_VERSION ?= $(VERSION)
@ -124,6 +115,7 @@ FRONTEND_SOURCES := $(shell find web_src/js web_src/css -type f)
FRONTEND_CONFIGS := vite.config.ts tailwind.config.ts
FRONTEND_DEST := public/assets/.vite/manifest.json
FRONTEND_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/.vite
FRONTEND_DEV_LOG_LEVEL ?= warn
BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
@ -293,33 +285,33 @@ lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backen
.PHONY: lint-js
lint-js: node_modules ## lint js and ts files
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES)
$(NODE_VARS) pnpm exec vue-tsc
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES)
pnpm exec vue-tsc
.PHONY: lint-js-fix
lint-js-fix: node_modules ## lint js and ts files and fix issues
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES) --fix
$(NODE_VARS) pnpm exec vue-tsc
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES) --fix
pnpm exec vue-tsc
.PHONY: lint-css
lint-css: node_modules ## lint css files
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES)
pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES)
.PHONY: lint-css-fix
lint-css-fix: node_modules ## lint css files and fix issues
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
.PHONY: lint-swagger
lint-swagger: node_modules ## lint swagger files
$(NODE_VARS) pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC)
pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC)
.PHONY: lint-md
lint-md: node_modules ## lint markdown files
$(NODE_VARS) pnpm exec markdownlint *.md
pnpm exec markdownlint *.md
.PHONY: lint-md-fix
lint-md-fix: node_modules ## lint markdown files and fix issues
$(NODE_VARS) pnpm exec markdownlint --fix *.md
pnpm exec markdownlint --fix *.md
.PHONY: lint-spell
lint-spell: ## lint spelling
@ -369,11 +361,11 @@ lint-yaml: .venv ## lint yaml files
.PHONY: lint-json
lint-json: node_modules ## lint json files
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY)
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY)
.PHONY: lint-json-fix
lint-json-fix: node_modules ## lint and fix json files
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) --fix
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) --fix
.PHONY: watch
watch: ## watch everything and continuously rebuild
@ -381,7 +373,7 @@ watch: ## watch everything and continuously rebuild
.PHONY: watch-frontend
watch-frontend: node_modules ## start vite dev server for frontend
NODE_ENV=development $(NODE_VARS) pnpm exec vite
NODE_ENV=development pnpm exec vite --logLevel $(FRONTEND_DEV_LOG_LEVEL)
.PHONY: watch-backend
watch-backend: ## watch backend files and continuously rebuild
@ -397,7 +389,7 @@ test-backend: ## test backend files
.PHONY: test-frontend
test-frontend: node_modules ## test frontend files
$(NODE_VARS) pnpm exec vitest
pnpm exec vitest
.PHONY: test-check
test-check:
@ -533,7 +525,7 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
.PHONY: playwright
playwright: deps-frontend
@# on GitHub Actions VMs, playwright's system deps are pre-installed
@$(NODE_VARS) pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium $(if $(CI),firefox) $(PLAYWRIGHT_FLAGS)
@pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium $(if $(CI),firefox) $(PLAYWRIGHT_FLAGS)
.PHONY: test-e2e
test-e2e: playwright $(EXECUTABLE_E2E)
@ -749,7 +741,7 @@ deps-tools: ## install tool dependencies
wait
node_modules: pnpm-lock.yaml
$(NODE_VARS) pnpm install --frozen-lockfile
pnpm install --frozen-lockfile
@touch node_modules
.venv: uv.lock
@ -757,20 +749,25 @@ node_modules: pnpm-lock.yaml
@touch .venv
.PHONY: update
update: update-js update-py ## update js and py dependencies
update: update-go update-js update-py ## update dependencies
.PHONY: update-go
update-go: ## update go dependencies
$(GO) get -u ./...
$(MAKE) tidy
.PHONY: update-js
update-js: node_modules ## update js dependencies
$(NODE_VARS) pnpm exec updates -u -f package.json
pnpm exec updates -u -f package.json
rm -rf node_modules pnpm-lock.yaml
$(NODE_VARS) pnpm install
$(NODE_VARS) pnpm exec nolyfill install
$(NODE_VARS) pnpm install
pnpm install
pnpm exec nolyfill install
pnpm install
@touch node_modules
.PHONY: update-py
update-py: node_modules ## update py dependencies
$(NODE_VARS) pnpm exec updates -u -f pyproject.toml
pnpm exec updates -u -f pyproject.toml
rm -rf .venv uv.lock
uv sync
@touch .venv
@ -782,7 +779,7 @@ $(FRONTEND_DEST): $(FRONTEND_SOURCES) $(FRONTEND_CONFIGS) pnpm-lock.yaml
@$(MAKE) -s node_modules
@rm -rf $(FRONTEND_DEST_ENTRIES)
@echo "Running vite build..."
@$(NODE_VARS) pnpm exec vite build
@pnpm exec vite build
@touch $(FRONTEND_DEST)
.PHONY: svg
@ -802,7 +799,7 @@ svg-check: svg
.PHONY: lockfile-check
lockfile-check:
$(NODE_VARS) pnpm install --frozen-lockfile
pnpm install --frozen-lockfile
@diff=$$(git diff --color=always pnpm-lock.yaml); \
if [ -n "$$diff" ]; then \
echo "pnpm-lock.yaml is inconsistent with package.json"; \

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@ import (
"strconv"
"strings"
"github.com/google/go-github/v74/github"
"github.com/google/go-github/v84/github"
"github.com/urfave/cli/v3"
"gopkg.in/yaml.v3"
)

View File

@ -329,7 +329,7 @@ export default defineConfig([
'github/no-innerText': [2],
'github/no-then': [2],
'github/no-useless-passive': [2],
'github/prefer-observers': [2],
'github/prefer-observers': [0],
'github/require-passive-events': [2],
'github/unescaped-html-literal': [2],
'grouped-accessor-pairs': [2],
@ -766,6 +766,7 @@ export default defineConfig([
'unicorn/catch-error-name': [0],
'unicorn/consistent-destructuring': [2],
'unicorn/consistent-empty-array-spread': [2],
'unicorn/consistent-template-literal-escape': [2],
'unicorn/consistent-existence-index-check': [0],
'unicorn/consistent-function-scoping': [0],
'unicorn/custom-error-definition': [0],
@ -821,6 +822,7 @@ export default defineConfig([
'unicorn/no-unused-properties': [2],
'unicorn/no-useless-collection-argument': [2],
'unicorn/no-useless-fallback-in-spread': [2],
'unicorn/no-useless-iterator-to-array': [2],
'unicorn/no-useless-length-check': [2],
'unicorn/no-useless-promise-resolve-reject': [2],
'unicorn/no-useless-spread': [2],
@ -870,6 +872,7 @@ export default defineConfig([
'unicorn/prefer-response-static-json': [2],
'unicorn/prefer-set-has': [0],
'unicorn/prefer-set-size': [2],
'unicorn/prefer-simple-condition-first': [0],
'unicorn/prefer-spread': [0],
'unicorn/prefer-string-raw': [0],
'unicorn/prefer-string-replace-all': [0],
@ -888,6 +891,7 @@ export default defineConfig([
'unicorn/require-post-message-target-origin': [0],
'unicorn/string-content': [0],
'unicorn/switch-case-braces': [0],
'unicorn/switch-case-break-position': [2],
'unicorn/template-indent': [2],
'unicorn/text-encoding-identifier-case': [0],
'unicorn/throw-new-error': [2],
@ -1013,7 +1017,7 @@ export default defineConfig([
},
},
{
files: ['*', 'tools/**/*'],
files: ['*', 'tools/**/*', 'tests/**/*'],
languageOptions: {globals: globals.nodeBuiltin},
},
{

217
go.mod
View File

@ -9,7 +9,7 @@ godebug x509negativeserial=1
require (
code.gitea.io/actions-proto-go v0.4.1
code.gitea.io/sdk/gitea v0.23.2
code.gitea.io/sdk/gitea v0.24.1
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.19.1
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
@ -18,22 +18,22 @@ require (
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/httpsig v1.2.3
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
github.com/42wim/httpsig v1.2.4
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.3.0
github.com/PuerkitoBio/goquery v1.11.0
github.com/Azure/go-ntlmssp v0.1.0
github.com/ProtonMail/go-crypto v1.4.1
github.com/PuerkitoBio/goquery v1.12.0
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
github.com/alecthomas/chroma/v2 v2.23.1
github.com/aws/aws-sdk-go-v2/credentials v1.19.7
github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.8
github.com/aws/aws-sdk-go-v2/credentials v1.19.13
github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.12
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.5.7
github.com/bohde/codel v0.2.0
github.com/buildkite/terminal-to-html/v3 v3.16.8
github.com/caddyserver/certmagic v0.25.1
github.com/caddyserver/certmagic v0.25.2
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21
github.com/chi-middleware/proxy v1.1.1
github.com/coder/websocket v1.8.14
@ -50,42 +50,42 @@ require (
github.com/gliderlabs/ssh v0.3.8
github.com/go-chi/chi/v5 v5.2.5
github.com/go-chi/cors v1.2.2
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.4
github.com/go-git/go-billy/v5 v5.7.0
github.com/go-git/go-git/v5 v5.16.5
github.com/go-ldap/ldap/v3 v3.4.12
github.com/go-redsync/redsync/v4 v4.15.0
github.com/go-co-op/gocron/v2 v2.19.1
github.com/go-enry/go-enry/v2 v2.9.5
github.com/go-git/go-billy/v5 v5.8.0
github.com/go-git/go-git/v5 v5.17.2
github.com/go-ldap/ldap/v3 v3.4.13
github.com/go-redsync/redsync/v4 v4.16.0
github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.4
github.com/goccy/go-json v0.10.5
github.com/go-webauthn/webauthn v0.16.1
github.com/goccy/go-json v0.10.6
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/go-github/v74 v74.0.0
github.com/google/go-github/v84 v84.0.0
github.com/google/licenseclassifier/v2 v2.0.0
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
github.com/hashicorp/go-version v1.8.0
github.com/hashicorp/go-version v1.9.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime v1.3.0
github.com/jhillyerd/enmime/v2 v2.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.3
github.com/klauspost/compress v1.18.5
github.com/klauspost/cpuid/v2 v2.3.0
github.com/lib/pq v1.11.1
github.com/lib/pq v1.12.1
github.com/markbates/goth v1.82.0
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-sqlite3 v1.14.33
github.com/meilisearch/meilisearch-go v0.36.0
github.com/mattn/go-sqlite3 v1.14.38
github.com/meilisearch/meilisearch-go v0.36.1
github.com/mholt/archives v0.1.5
github.com/microcosm-cc/bluemonday v1.0.27
github.com/microsoft/go-mssqldb v1.9.6
github.com/minio/minio-go/v7 v7.0.98
github.com/msteinert/pam v1.2.0
github.com/minio/minio-go/v7 v7.0.99
github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.63
github.com/niklasfasching/go-org v1.9.1
github.com/olivere/elastic/v7 v7.0.32
@ -94,38 +94,38 @@ require (
github.com/pquerna/otp v1.5.0
github.com/prometheus/client_golang v1.23.2
github.com/quasoft/websspi v1.1.2
github.com/redis/go-redis/v9 v9.17.3
github.com/redis/go-redis/v9 v9.18.0
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.4.0
github.com/stretchr/testify v1.11.1
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.15
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
github.com/urfave/cli-docs/v3 v3.1.0
github.com/urfave/cli/v3 v3.4.1
github.com/wneessen/go-mail v0.7.2
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.16
github.com/yuin/goldmark v1.8.2
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
gitlab.com/gitlab-org/api/client-go v0.142.4
gitlab.com/gitlab-org/api/client-go v1.46.0
go.yaml.in/yaml/v4 v4.0.0-rc.3
golang.org/x/crypto v0.47.0
golang.org/x/image v0.35.0
golang.org/x/net v0.49.0
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0
golang.org/x/sys v0.40.0
golang.org/x/text v0.33.0
google.golang.org/grpc v1.78.0
golang.org/x/crypto v0.49.0
golang.org/x/image v0.38.0
golang.org/x/net v0.52.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
golang.org/x/sys v0.42.0
golang.org/x/text v0.35.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
gopkg.in/ini.v1 v1.67.1
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.6.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.11
)
@ -134,75 +134,78 @@ require (
cloud.google.com/go/compute/metadata v0.9.0 // indirect
code.gitea.io/gitea-vet v0.2.3 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring/v2 v2.10.0 // indirect
github.com/RoaringBitmap/roaring/v2 v2.16.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aws/aws-sdk-go-v2 v1.41.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/smithy-go v1.24.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.11 // indirect
github.com/blevesearch/geo v0.2.4 // indirect
github.com/blevesearch/go-faiss v1.0.26 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/blevesearch/bleve_index_api v1.3.7 // indirect
github.com/blevesearch/geo v0.2.5 // indirect
github.com/blevesearch/go-faiss v1.0.30 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect
github.com/blevesearch/mmap-go v1.2.0 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.4.5 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.8 // indirect
github.com/blevesearch/vellum v1.2.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.3 // indirect
github.com/blevesearch/zapx/v12 v12.4.3 // indirect
github.com/blevesearch/zapx/v13 v13.4.3 // indirect
github.com/blevesearch/zapx/v14 v14.4.3 // indirect
github.com/blevesearch/zapx/v15 v15.4.3 // indirect
github.com/blevesearch/zapx/v16 v16.3.2 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/boombuler/barcode v1.1.0 // indirect
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.4 // indirect
github.com/caddyserver/zerossl v0.1.5 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
github.com/couchbase/gomemcached v0.3.3 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/couchbase/gomemcached v0.3.4 // indirect
github.com/couchbase/goutils v0.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-webauthn/x v0.1.24 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/go-webauthn/x v0.2.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.5 // indirect
github.com/google/flatbuffers v25.12.19+incompatible // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/go-tpm v0.9.8 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
@ -210,55 +213,55 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/inbucket/html2text v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/libdns/libdns v1.1.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mailru/easyjson v0.9.2 // indirect
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mholt/acmez/v3 v3.1.4 // indirect
github.com/miekg/dns v1.1.69 // indirect
github.com/mholt/acmez/v3 v3.1.6 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minlz v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/minio/minlz v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/olekukonko/cat v0.0.0-20250817074551-3280053e4e00 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.1.0 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/nwaples/rardecode/v2 v2.2.2 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.2.0 // indirect
github.com/olekukonko/ll v0.1.8 // indirect
github.com/olekukonko/tablewriter v1.1.4 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.4.0 // indirect
github.com/pierrec/lz4/v4 v4.1.26 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/rhysd/actionlint v1.7.11 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/rhysd/actionlint v1.7.12 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/tinylib/msgp v1.6.1 // indirect
github.com/tinylib/msgp v1.6.3 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
@ -272,14 +275,14 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401020348-3a24fdc17823 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
@ -289,16 +292,22 @@ ignore (
./node_modules
)
replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347
// When doing "go get -u ./...", Golang will try to update all dependencies
// But not all latest versions of dependencies are compatible with other packages or our codebase, so we need to pin some dependencies to specific versions
// Need to regularly maintain this list to try to update them to latest versions, especially the TODO ones
replace github.com/nektos/act => gitea.com/gitea/act v0.261.10
replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 // jaytaylor/html2text is unmaintained
exclude github.com/gofrs/uuid v3.2.0+incompatible
replace github.com/nektos/act => gitea.com/gitea/act v0.261.10 // gitea maintains its own package
exclude github.com/gofrs/uuid v4.0.0+incompatible
replace github.com/urfave/cli/v3 => github.com/urfave/cli/v3 v3.4.1 // v3.6.2 breaks -c flag parsing in help commands
exclude github.com/goccy/go-json v0.4.11
replace go.yaml.in/yaml/v4 => go.yaml.in/yaml/v4 v4.0.0-rc.3 // rc.4 changes block scalar serialization, wait for stable release
exclude github.com/satori/go.uuid v1.2.0
replace github.com/Azure/azure-sdk-for-go/sdk/azcore => github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 // v1.21.0+ uses API version unsupported by Azurite in CI
replace github.com/Azure/azure-sdk-for-go/sdk/storage/azblob => github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 // v1.6.4+ uses API version unsupported by Azurite in CI
replace github.com/microsoft/go-mssqldb => github.com/microsoft/go-mssqldb v1.9.7 // downgraded with Azure SDK
tool code.gitea.io/gitea-vet

628
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -139,7 +139,7 @@ func Test_CalcCommitStatus(t *testing.T) {
},
},
expected: &git_model.CommitStatus{
State: commitstatus.CommitStatusPending,
State: commitstatus.CommitStatusFailure,
},
},
{

View File

@ -64,36 +64,6 @@ func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID i
return result, nil
}
// LoadIssuesFromColumn load issues assigned to this column
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *IssuesOptions) (IssueList, error) {
issueList, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = b.ID
o.ProjectID = b.ProjectID
o.SortType = "project-column-sorting"
}))
if err != nil {
return nil, err
}
if b.Default {
issues, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = db.NoConditionID
o.ProjectID = b.ProjectID
o.SortType = "project-column-sorting"
}))
if err != nil {
return nil, err
}
issueList = append(issueList, issues...)
}
if err := issueList.LoadComments(ctx); err != nil {
return nil, err
}
return issueList, nil
}
// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {

View File

@ -404,6 +404,7 @@ func prepareMigrationTasks() []*migration {
newMigration(327, "Add disabled state to action runners", v1_26.AddDisabledToActionRunner),
newMigration(328, "Add TokenPermissions column to ActionRunJob", v1_26.AddTokenPermissionsToActionRunJob),
newMigration(329, "Add unique constraint for user badge", v1_26.AddUniqueIndexForUserBadge),
newMigration(330, "Add name column to webhook", v1_26.AddNameToWebhook),
}
return preparedMigrations
}

View File

@ -0,0 +1,16 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_26
import (
"xorm.io/xorm"
)
func AddNameToWebhook(x *xorm.Engine) error {
type Webhook struct {
Name string `xorm:"VARCHAR(255) NOT NULL DEFAULT ''"`
}
_, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(Webhook))
return err
}

View File

@ -257,9 +257,12 @@ func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) {
return columns, nil
}
// getDefaultColumn return default column and ensure only one exists
func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
// getDefaultColumnWithFallback return default column if one exists
// otherwise return the first column by sorting and set it as default column
func (p *Project) getDefaultColumnWithFallback(ctx context.Context) (*Column, error) {
var column Column
// try to find a column "default=true"
has, err := db.GetEngine(ctx).
Where("project_id=? AND `default` = ?", p.ID, true).
Desc("id").Get(&column)
@ -270,23 +273,9 @@ func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
if has {
return &column, nil
}
return nil, ErrProjectColumnNotExist{ColumnID: 0}
}
// MustDefaultColumn returns the default column for a project.
// If one exists, it is returned
// If none exists, the first column will be elevated to the default column of this project
func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
c, err := p.getDefaultColumn(ctx)
if err != nil && !IsErrProjectColumnNotExist(err) {
return nil, err
}
if c != nil {
return c, nil
}
var column Column
has, err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
// try to find the first column by sorting
has, err = db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
if err != nil {
return nil, err
}
@ -298,8 +287,24 @@ func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
return &column, nil
}
return nil, ErrProjectColumnNotExist{ColumnID: 0}
}
// MustDefaultColumn returns the default column for a project.
// If one exists, it is returned
// If none exists, the first column will be elevated to the default column of this project
// If there is no column, it creates a default column and returns it
func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
c, err := p.getDefaultColumnWithFallback(ctx)
if err != nil && !IsErrProjectColumnNotExist(err) {
return nil, err
}
if c != nil {
return c, nil
}
// create a default column if none is found
column = Column{
column := Column{
ProjectID: p.ID,
Default: true,
Title: "Uncategorized",

View File

@ -126,6 +126,7 @@ type Webhook struct {
OwnerID int64 `xorm:"INDEX"`
IsSystemWebhook bool
URL string `xorm:"url TEXT"`
Name string `xorm:"VARCHAR(255) NOT NULL DEFAULT ''"`
HTTPMethod string `xorm:"http_method"`
ContentType HookContentType
Secret string `xorm:"TEXT"`

View File

@ -135,7 +135,7 @@ func rewriteSubExpression(in string, forceFormat bool) (string, error) {
exprStart := -1
strStart := -1
var results []string
formatOut := ""
var formatOut strings.Builder
for pos < len(in) {
if strStart > -1 {
matches := strPattern.FindStringIndex(in[pos:])
@ -158,7 +158,7 @@ func rewriteSubExpression(in string, forceFormat bool) (string, error) {
}
if exprEnd > -1 {
formatOut += fmt.Sprintf("{%d}", len(results))
fmt.Fprintf(&formatOut, "{%d}", len(results))
results = append(results, strings.TrimSpace(in[exprStart:pos+exprEnd]))
pos += exprEnd + 2
exprStart = -1
@ -170,20 +170,20 @@ func rewriteSubExpression(in string, forceFormat bool) (string, error) {
} else {
exprStart = strings.Index(in[pos:], "${{")
if exprStart != -1 {
formatOut += escapeFormatString(in[pos : pos+exprStart])
formatOut.WriteString(escapeFormatString(in[pos : pos+exprStart]))
exprStart = pos + exprStart + 3
pos = exprStart
} else {
formatOut += escapeFormatString(in[pos:])
formatOut.WriteString(escapeFormatString(in[pos:]))
pos = len(in)
}
}
}
if len(results) == 1 && formatOut == "{0}" && !forceFormat {
if len(results) == 1 && formatOut.String() == "{0}" && !forceFormat {
return in, nil
}
out := fmt.Sprintf("format('%s', %s)", strings.ReplaceAll(formatOut, "'", "''"), strings.Join(results, ", "))
out := fmt.Sprintf("format('%s', %s)", strings.ReplaceAll(formatOut.String(), "'", "''"), strings.Join(results, ", "))
return out, nil
}

View File

@ -9,7 +9,9 @@ import (
"io/fs"
"os"
"path/filepath"
"slices"
"sort"
"strings"
"time"
"code.gitea.io/gitea/modules/container"
@ -61,6 +63,8 @@ type LayeredFS struct {
layers []*Layer
}
var _ fs.ReadDirFS = (*LayeredFS)(nil)
// Layered returns a new LayeredFS with the given layers. The first layer is the top layer.
func Layered(layers ...*Layer) *LayeredFS {
return &LayeredFS{layers: layers}
@ -83,6 +87,27 @@ func (l *LayeredFS) ReadFile(elems ...string) ([]byte, error) {
return bs, err
}
func (l *LayeredFS) ReadDir(name string) (files []fs.DirEntry, _ error) {
filesMap := map[string]fs.DirEntry{}
for _, layer := range l.layers {
entries, err := readDirOptional(layer, name)
if err != nil {
return nil, err
}
for _, entry := range entries {
entryName := entry.Name()
if _, exist := filesMap[entryName]; !exist && shouldInclude(entry) {
filesMap[entryName] = entry
}
}
}
for _, file := range filesMap {
files = append(files, file)
}
slices.SortFunc(files, func(a, b fs.DirEntry) int { return strings.Compare(a.Name(), b.Name()) })
return files, nil
}
// ReadLayeredFile reads the named file, and returns the layer name.
func (l *LayeredFS) ReadLayeredFile(elems ...string) ([]byte, string, error) {
name := util.PathJoinRel(elems...)

View File

@ -8,7 +8,7 @@ package pam
import (
"errors"
"github.com/msteinert/pam"
"github.com/msteinert/pam/v2"
)
// Supported is true when built with PAM
@ -28,6 +28,7 @@ func Auth(serviceName, userName, passwd string) (string, error) {
if err != nil {
return "", err
}
defer t.End()
if err = t.Authenticate(0); err != nil {
return "", err

View File

@ -61,16 +61,17 @@ type CommitStatusStates []CommitStatusState //nolint:revive // export stutter
// According to https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference
// > Additionally, a combined state is returned. The state is one of:
// > failure if any of the contexts report as error or failure
// > failure if any of the contexts report as warning (Gitea specific behavior)
// > pending if there are no statuses or a context is pending
// > success if the latest status for all contexts is success
func (css CommitStatusStates) Combine() CommitStatusState {
successCnt := 0
for _, state := range css {
switch {
case state.IsError() || state.IsFailure():
case state.IsError() || state.IsFailure() || state.IsWarning():
return CommitStatusFailure
case state.IsPending():
case state.IsSuccess() || state.IsWarning() || state.IsSkipped():
case state.IsSuccess() || state.IsSkipped():
successCnt++
}
}

View File

@ -41,7 +41,7 @@ func TestCombine(t *testing.T) {
{
name: "warning",
states: CommitStatusStates{CommitStatusWarning},
expected: CommitStatusSuccess,
expected: CommitStatusFailure,
},
// 2 states
{
@ -62,7 +62,7 @@ func TestCombine(t *testing.T) {
{
name: "pending and warning",
states: CommitStatusStates{CommitStatusPending, CommitStatusWarning},
expected: CommitStatusPending,
expected: CommitStatusFailure,
},
{
name: "success and error",
@ -77,7 +77,7 @@ func TestCombine(t *testing.T) {
{
name: "success and warning",
states: CommitStatusStates{CommitStatusSuccess, CommitStatusWarning},
expected: CommitStatusSuccess,
expected: CommitStatusFailure,
},
{
name: "error and failure",
@ -98,7 +98,7 @@ func TestCombine(t *testing.T) {
{
name: "pending, success and warning",
states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusWarning},
expected: CommitStatusPending,
expected: CommitStatusFailure,
},
{
name: "pending, success and error",
@ -133,7 +133,7 @@ func TestCombine(t *testing.T) {
{
name: "success, warning and skipped",
states: CommitStatusStates{CommitStatusSuccess, CommitStatusWarning, CommitStatusSkipped},
expected: CommitStatusSuccess,
expected: CommitStatusFailure,
},
// All success
{
@ -181,12 +181,12 @@ func TestCombine(t *testing.T) {
{
name: "mixed states with all success",
states: CommitStatusStates{CommitStatusSuccess, CommitStatusSuccess, CommitStatusPending, CommitStatusWarning},
expected: CommitStatusPending,
expected: CommitStatusFailure,
},
{
name: "all success with warning",
states: CommitStatusStates{CommitStatusSuccess, CommitStatusSuccess, CommitStatusSuccess, CommitStatusWarning},
expected: CommitStatusSuccess,
expected: CommitStatusFailure,
},
}

View File

@ -12,10 +12,17 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/santhosh-tekuri/jsonschema/v6"
"gopkg.in/yaml.v3"
)
// schemaLoader implements jsonschema.URLLoader
type schemaLoader struct{}
func (l *schemaLoader) Load(url string) (any, error) {
return openSchema(url)
}
// Load project data from file, with optional validation
func Load(filename string, data any, validation bool) error {
isJSON := strings.HasSuffix(filename, ".json")
@ -43,7 +50,7 @@ func unmarshal(bs []byte, data any, isJSON bool) error {
func getSchema(filename string) (*jsonschema.Schema, error) {
c := jsonschema.NewCompiler()
c.LoadURL = openSchema
c.UseLoader(&schemaLoader{})
return c.Compile(filename)
}

View File

@ -7,7 +7,7 @@ import (
"strings"
"testing"
"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/santhosh-tekuri/jsonschema/v6"
"github.com/stretchr/testify/assert"
)

View File

@ -8,7 +8,6 @@
package migration
import (
"io"
"io/fs"
"path"
"sync"
@ -16,6 +15,8 @@ import (
_ "embed"
"code.gitea.io/gitea/modules/assetfs"
"github.com/santhosh-tekuri/jsonschema/v6"
)
//go:embed bindata.dat
@ -25,6 +26,11 @@ var BuiltinAssets = sync.OnceValue(func() fs.FS {
return assetfs.NewEmbeddedFS(bindata)
})
func openSchema(filename string) (io.ReadCloser, error) {
return BuiltinAssets().Open(path.Base(filename))
func openSchema(filename string) (any, error) {
f, err := BuiltinAssets().Open(path.Base(filename))
if err != nil {
return nil, err
}
defer f.Close()
return jsonschema.UnmarshalJSON(f)
}

View File

@ -6,14 +6,15 @@
package migration
import (
"io"
"net/url"
"os"
"path"
"path/filepath"
"github.com/santhosh-tekuri/jsonschema/v6"
)
func openSchema(s string) (io.ReadCloser, error) {
func openSchema(s string) (any, error) {
u, err := url.Parse(s)
if err != nil {
return nil, err
@ -34,5 +35,10 @@ func openSchema(s string) (io.ReadCloser, error) {
filename = filepath.Join("modules/migration/schemas", basename)
}
}
return os.Open(filename)
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return jsonschema.UnmarshalJSON(f)
}

View File

@ -140,7 +140,7 @@ type nuspecPackage struct {
func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) {
archive, err := zip.NewReader(r, size)
if err != nil {
return nil, err
return nil, util.NewInvalidArgumentErrorf("unable to parse package meta: %v", err)
}
for _, file := range archive.File {

View File

@ -42,7 +42,7 @@ func (l PortablePdbList) Close() {
func ExtractPortablePdb(r io.ReaderAt, size int64) (PortablePdbList, error) {
archive, err := zip.NewReader(r, size)
if err != nil {
return nil, err
return nil, util.NewInvalidArgumentErrorf("unable to extract portable pdb: %v", err)
}
var pdbs PortablePdbList

View File

@ -13,6 +13,7 @@ import (
"sync/atomic"
"time"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/routing"
@ -22,24 +23,29 @@ const viteDevPortFile = "public/assets/.vite/dev-port"
var viteDevProxy atomic.Pointer[httputil.ReverseProxy]
func getViteDevServerBaseURL() string {
portFile := filepath.Join(setting.StaticRootPath, viteDevPortFile)
portContent, _ := os.ReadFile(portFile)
port := strings.TrimSpace(string(portContent))
if port == "" {
return ""
}
return "http://localhost:" + port
}
func getViteDevProxy() *httputil.ReverseProxy {
if proxy := viteDevProxy.Load(); proxy != nil {
return proxy
}
portFile := filepath.Join(setting.StaticRootPath, viteDevPortFile)
data, err := os.ReadFile(portFile)
if err != nil {
return nil
}
port := strings.TrimSpace(string(data))
if port == "" {
viteDevServerBaseURL := getViteDevServerBaseURL()
if viteDevServerBaseURL == "" {
return nil
}
target, err := url.Parse("http://localhost:" + port)
target, err := url.Parse(viteDevServerBaseURL)
if err != nil {
log.Error("Failed to parse Vite dev server URL: %v", err)
log.Error("Failed to parse Vite dev server base URL %s, err: %v", viteDevServerBaseURL, err)
return nil
}
@ -60,7 +66,7 @@ func getViteDevProxy() *httputil.ReverseProxy {
ModifyResponse: func(resp *http.Response) error {
// add a header to indicate the Vite dev server port,
// make developers know that this request is proxied to Vite dev server and which port it is
resp.Header.Add("X-Gitea-Vite-Port", port)
resp.Header.Add("X-Gitea-Vite-Dev-Server", viteDevServerBaseURL)
return nil
},
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
@ -92,19 +98,46 @@ func ViteDevMiddleware(next http.Handler) http.Handler {
})
}
// isViteDevMode returns true if the Vite dev server port file exists.
// In production mode, the result is cached after the first check.
func isViteDevMode() bool {
var viteDevModeCheck atomic.Pointer[struct {
isDev bool
time time.Time
}]
// IsViteDevMode returns true if the Vite dev server port file exists and the server is alive
func IsViteDevMode() bool {
if setting.IsProd {
return false
}
portFile := filepath.Join(setting.StaticRootPath, viteDevPortFile)
_, err := os.Stat(portFile)
return err == nil
now := time.Now()
lastCheck := viteDevModeCheck.Load()
if lastCheck != nil && time.Now().Sub(lastCheck.time) < time.Second {
return lastCheck.isDev
}
viteDevServerBaseURL := getViteDevServerBaseURL()
if viteDevServerBaseURL == "" {
return false
}
req := httplib.NewRequest(viteDevServerBaseURL+"/web_src/js/__vite_dev_server_check", "GET")
resp, _ := req.Response()
if resp != nil {
_ = resp.Body.Close()
}
isDev := resp != nil && resp.StatusCode == http.StatusOK
viteDevModeCheck.Store(&struct {
isDev bool
time time.Time
}{
isDev: isDev,
time: now,
})
return isDev
}
func viteDevSourceURL(name string) string {
if !isViteDevMode() {
if !IsViteDevMode() {
return ""
}
if strings.HasPrefix(name, "css/theme-") {

View File

@ -19,6 +19,8 @@ var ErrInvalidReceiveHook = errors.New("Invalid JSON payload received over webho
type Hook struct {
// The unique identifier of the webhook
ID int64 `json:"id"`
// Optional human-readable name for the webhook
Name string `json:"name"`
// The type of the webhook (e.g., gitea, slack, discord)
Type string `json:"type"`
// Branch filter pattern to determine which branches trigger the webhook
@ -66,6 +68,8 @@ type CreateHookOption struct {
// default: false
// Whether the webhook should be active upon creation
Active bool `json:"active"`
// Optional human-readable name for the webhook
Name string `json:"name" binding:"MaxSize(255)"`
}
// EditHookOption options when modify one hook
@ -80,6 +84,8 @@ type EditHookOption struct {
AuthorizationHeader string `json:"authorization_header"`
// Whether the webhook is active and will be triggered
Active *bool `json:"active"`
// Optional human-readable name
Name *string `json:"name,omitzero" binding:"MaxSize(255)"`
}
// Payloader payload is some part of one hook

View File

@ -81,6 +81,8 @@ type Issue struct {
Repo *RepositoryMeta `json:"repository"`
PinOrder int `json:"pin_order"`
// The version of the issue content for optimistic locking
ContentVersion int `json:"content_version"`
}
// CreateIssueOption options to create one issue
@ -114,6 +116,8 @@ type EditIssueOption struct {
// swagger:strfmt date-time
Deadline *time.Time `json:"due_date"`
RemoveDeadline *bool `json:"unset_due_date"`
// The current version of the issue content to detect conflicts during editing
ContentVersion *int `json:"content_version"`
}
// EditDeadlineOption options for creating a deadline

View File

@ -91,6 +91,8 @@ type PullRequest struct {
// The pin order for the pull request
PinOrder int `json:"pin_order"`
// The version of the pull request content for optimistic locking
ContentVersion int `json:"content_version"`
}
// PRBranchInfo information about a branch
@ -168,6 +170,8 @@ type EditPullRequestOption struct {
RemoveDeadline *bool `json:"unset_due_date"`
// Whether to allow maintainer edits
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
// The current version of the pull request content to detect conflicts during editing
ContentVersion *int `json:"content_version"`
}
// ChangedFile store information about files affected by the pull request

View File

@ -53,11 +53,11 @@ func (lc *LogChecker) checkLogEvent(event *log.EventFormatted) {
}
}
var checkerIndex int64
var checkerIndex atomic.Int64
func NewLogChecker(namePrefix string) (logChecker *LogChecker, cancel func()) {
logger := log.GetManager().GetLogger(namePrefix)
newCheckerIndex := atomic.AddInt64(&checkerIndex, 1)
newCheckerIndex := checkerIndex.Add(1)
writerName := namePrefix + "-" + strconv.FormatInt(newCheckerIndex, 10)
lc := &LogChecker{}

View File

@ -12,19 +12,19 @@ import (
)
func TestDebounce(t *testing.T) {
var c int64
var c atomic.Int64
d := Debounce(50 * time.Millisecond)
d(func() { atomic.AddInt64(&c, 1) })
assert.EqualValues(t, 0, atomic.LoadInt64(&c))
d(func() { atomic.AddInt64(&c, 1) })
d(func() { atomic.AddInt64(&c, 1) })
d(func() { c.Add(1) })
assert.EqualValues(t, 0, c.Load())
d(func() { c.Add(1) })
d(func() { c.Add(1) })
time.Sleep(100 * time.Millisecond)
assert.EqualValues(t, 1, atomic.LoadInt64(&c))
d(func() { atomic.AddInt64(&c, 1) })
assert.EqualValues(t, 1, atomic.LoadInt64(&c))
d(func() { atomic.AddInt64(&c, 1) })
d(func() { atomic.AddInt64(&c, 1) })
d(func() { atomic.AddInt64(&c, 1) })
assert.EqualValues(t, 1, c.Load())
d(func() { c.Add(1) })
assert.EqualValues(t, 1, c.Load())
d(func() { c.Add(1) })
d(func() { c.Add(1) })
d(func() { c.Add(1) })
time.Sleep(100 * time.Millisecond)
assert.EqualValues(t, 2, atomic.LoadInt64(&c))
assert.EqualValues(t, 2, c.Load())
}

View File

@ -7,6 +7,7 @@ import (
"context"
"time"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
)
@ -36,5 +37,6 @@ func CommonTemplateContextData() reqctx.ContextData {
"PageStartTime": time.Now(),
"RunModeIsProd": setting.IsProd,
"ViteModeIsDev": public.IsViteDevMode(),
}
}

View File

@ -1155,6 +1155,11 @@
"_dockerhub": "folder-docker",
"-dockerhub": "folder-docker",
"__dockerhub__": "folder-docker",
"nginx": "folder-nginx",
".nginx": "folder-nginx",
"_nginx": "folder-nginx",
"-nginx": "folder-nginx",
"__nginx__": "folder-nginx",
"astro": "folder-astro",
".astro": "folder-astro",
"_astro": "folder-astro",
@ -1785,6 +1790,11 @@
"_pytest_cache": "folder-python",
"-pytest_cache": "folder-python",
"__pytest_cache__": "folder-python",
"r": "folder-r",
".r": "folder-r",
"_r": "folder-r",
"-r": "folder-r",
"__r__": "folder-r",
"sandbox": "folder-sandbox",
".sandbox": "folder-sandbox",
"_sandbox": "folder-sandbox",
@ -4517,7 +4527,37 @@
".forms": "folder-form",
"_forms": "folder-form",
"-forms": "folder-form",
"__forms__": "folder-form"
"__forms__": "folder-form",
"deprecated": "folder-deprecated",
".deprecated": "folder-deprecated",
"_deprecated": "folder-deprecated",
"-deprecated": "folder-deprecated",
"__deprecated__": "folder-deprecated",
"scrap": "folder-scrap",
".scrap": "folder-scrap",
"_scrap": "folder-scrap",
"-scrap": "folder-scrap",
"__scrap__": "folder-scrap",
"skill": "folder-skills",
".skill": "folder-skills",
"_skill": "folder-skills",
"-skill": "folder-skills",
"__skill__": "folder-skills",
"skills": "folder-skills",
".skills": "folder-skills",
"_skills": "folder-skills",
"-skills": "folder-skills",
"__skills__": "folder-skills",
"instruction": "folder-instructions",
".instruction": "folder-instructions",
"_instruction": "folder-instructions",
"-instruction": "folder-instructions",
"__instruction__": "folder-instructions",
"instructions": "folder-instructions",
".instructions": "folder-instructions",
"_instructions": "folder-instructions",
"-instructions": "folder-instructions",
"__instructions__": "folder-instructions"
},
"folderNamesExpanded": {
"rust": "folder-rust-open",
@ -5675,6 +5715,11 @@
"_dockerhub": "folder-docker-open",
"-dockerhub": "folder-docker-open",
"__dockerhub__": "folder-docker-open",
"nginx": "folder-nginx-open",
".nginx": "folder-nginx-open",
"_nginx": "folder-nginx-open",
"-nginx": "folder-nginx-open",
"__nginx__": "folder-nginx-open",
"astro": "folder-astro-open",
".astro": "folder-astro-open",
"_astro": "folder-astro-open",
@ -6305,6 +6350,11 @@
"_pytest_cache": "folder-python-open",
"-pytest_cache": "folder-python-open",
"__pytest_cache__": "folder-python-open",
"r": "folder-r-open",
".r": "folder-r-open",
"_r": "folder-r-open",
"-r": "folder-r-open",
"__r__": "folder-r-open",
"sandbox": "folder-sandbox-open",
".sandbox": "folder-sandbox-open",
"_sandbox": "folder-sandbox-open",
@ -9037,7 +9087,37 @@
".forms": "folder-form-open",
"_forms": "folder-form-open",
"-forms": "folder-form-open",
"__forms__": "folder-form-open"
"__forms__": "folder-form-open",
"deprecated": "folder-deprecated-open",
".deprecated": "folder-deprecated-open",
"_deprecated": "folder-deprecated-open",
"-deprecated": "folder-deprecated-open",
"__deprecated__": "folder-deprecated-open",
"scrap": "folder-scrap-open",
".scrap": "folder-scrap-open",
"_scrap": "folder-scrap-open",
"-scrap": "folder-scrap-open",
"__scrap__": "folder-scrap-open",
"skill": "folder-skills-open",
".skill": "folder-skills-open",
"_skill": "folder-skills-open",
"-skill": "folder-skills-open",
"__skill__": "folder-skills-open",
"skills": "folder-skills-open",
".skills": "folder-skills-open",
"_skills": "folder-skills-open",
"-skills": "folder-skills-open",
"__skills__": "folder-skills-open",
"instruction": "folder-instructions-open",
".instruction": "folder-instructions-open",
"_instruction": "folder-instructions-open",
"-instruction": "folder-instructions-open",
"__instruction__": "folder-instructions-open",
"instructions": "folder-instructions-open",
".instructions": "folder-instructions-open",
"_instructions": "folder-instructions-open",
"-instructions": "folder-instructions-open",
"__instructions__": "folder-instructions-open"
},
"rootFolderNames": {},
"rootFolderNamesExpanded": {},
@ -9063,6 +9143,7 @@
"json5": "json",
"jsonl": "json",
"ndjson": "json",
"schema.json": "json_schema",
"hjson": "hjson",
"jinja": "jinja",
"jinja2": "jinja",
@ -9261,6 +9342,7 @@
"vcxitems.filters": "visualstudio",
"vcxproj": "visualstudio",
"vcxproj.filters": "visualstudio",
"wixproj": "visualstudio",
"vcl": "varnish",
"pdb": "database",
"sql": "database",
@ -9984,6 +10066,7 @@
"ast": "sas",
"sast": "sas",
"nupkg": "nuget",
"nuspec": "nuget",
"command": "command",
"dsc": "denizenscript",
"code-search": "search",
@ -10206,6 +10289,10 @@
"lean": "lean",
"sls": "salt",
"m2": "macaulay2",
"skill.md": "skill",
"skills.md": "skill",
"instructions.md": "instructions",
"instruction.md": "instructions",
"cljx": "clojure",
"clojure": "clojure",
"edn": "clojure",
@ -10363,7 +10450,6 @@
"jmx": "xml",
"launch": "xml",
"menu": "xml",
"nuspec": "xml",
"opml": "xml",
"owl": "xml",
"proj": "xml",
@ -11094,7 +11180,18 @@
"rstest.config.ts": "rstack",
"rstest.config.mts": "rstack",
"rstest.config.cts": "rstack",
"rspress.config.js": "rstack",
"rspress.config.mjs": "rstack",
"rspress.config.cjs": "rstack",
"rspress.config.ts": "rstack",
"rspress.config.mts": "rstack",
"rspress.config.cts": "rstack",
"rslint.config.js": "rstack",
"rslint.config.mjs": "rstack",
"rslint.config.cjs": "rstack",
"rslint.config.ts": "rstack",
"rslint.config.mts": "rstack",
"rslint.config.cts": "rstack",
"rslint.json": "rstack",
"rslint.jsonc": "rstack",
"lynx.config.js": "lynx",
@ -11141,6 +11238,8 @@
".env.dist": "tune",
".env.prod": "tune",
".env.production": "tune",
".env.prod.example": "tune",
".env.production.example": "tune",
".env.stg": "tune",
".env.stage": "tune",
".env.staging": "tune",
@ -11850,6 +11949,12 @@
"velite.config.ts": "velite",
"velite.config.mts": "velite",
"velite.config.cts": "velite",
"rolldown.config.js": "rolldown",
"rolldown.config.mjs": "rolldown",
"rolldown.config.cjs": "rolldown",
"rolldown.config.ts": "rolldown",
"rolldown.config.mts": "rolldown",
"rolldown.config.cts": "rolldown",
"lerna.json": "lerna",
"windi.config.js": "windicss",
"windi.config.cjs": "windicss",
@ -12352,6 +12457,7 @@
"lefthookrc": "lefthook",
".github/labeler.yml": "label",
".github/labeler.yaml": "label",
"tags": "label",
"zeabur.json": "zeabur",
"zeabur.jsonc": "zeabur",
"zeabur.json5": "zeabur",
@ -12427,6 +12533,7 @@
".oxfmtrc.json": "oxc",
".oxfmtrc.jsonc": "oxc",
"oxlint.config.ts": "oxc",
"oxfmt.config.ts": "oxc",
"claude.md": "claude",
"claude.local.md": "claude",
".cursorignore": "cursor",
@ -12450,6 +12557,9 @@
".shellcheckrc": "shellcheck",
"shellcheckrc": "shellcheck",
"warp.md": "warp",
"skill.md": "skill",
"instructions.md": "instructions",
"instruction.md": "instructions",
"language-configuration.json": "jsonc",
"icon-theme.json": "jsonc",
"color-theme.json": "jsonc",
@ -12653,7 +12763,9 @@
"helm": "helm",
"nginx": "nginx",
"cue": "cue",
"lean": "lean"
"lean": "lean",
"skill": "skill",
"instructions": "instructions"
},
"light": {
"fileExtensions": {

View File

@ -319,6 +319,8 @@
"folder-decorators": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M23.66 30a7.8 7.8 0 0 1-3.737-.929 7.06 7.06 0 0 1-2.81-2.784 9.2 9.2 0 0 1-1.07-4.655 18.3 18.3 0 0 1 .863-5.874 12.6 12.6 0 0 1 2.349-4.267 10.1 10.1 0 0 1 3.392-2.604A9.3 9.3 0 0 1 26.607 8a5.22 5.22 0 0 1 4.101 1.455A5.64 5.64 0 0 1 32 13.347a5.4 5.4 0 0 1-.069.832q-.07.443-.153.914l-1.611 7.97h-2.308l-.029-1.006h-.11c-.464.258-.96.665-1.488.96a3.96 3.96 0 0 1-1.96.444 3.03 3.03 0 0 1-2.098-.818 2.79 2.79 0 0 1-.904-2.175 4.34 4.34 0 0 1 1.877-3.781 13 13 0 0 1 5.907-1.76 6 6 0 0 0 .167-1.22 3.94 3.94 0 0 0-.611-2.258 2.71 2.71 0 0 0-2.39-.9 4.9 4.9 0 0 0-2.42.692 7.7 7.7 0 0 0-2.266 2.051 10.9 10.9 0 0 0-1.682 3.367 15.3 15.3 0 0 0-.638 4.641 7.05 7.05 0 0 0 .721 3.366 5 5 0 0 0 1.864 2.009 4.67 4.67 0 0 0 2.39.665 4.4 4.4 0 0 0 1.668-.29 6.2 6.2 0 0 0 1.418-.818l1.279 2.272a7.3 7.3 0 0 1-2.21 1.122A9 9 0 0 1 23.66 30m2.018-9.056a2.8 2.8 0 0 0 1.04-.21 4.8 4.8 0 0 0 1.04-.574l.56-2.742a6.8 6.8 0 0 0-2.99.951 1.87 1.87 0 0 0-.772 1.512 1 1 0 0 0 .31.783 1.16 1.16 0 0 0 .812.28'/></svg>",
"folder-delta-open": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f8bbd0' d='M23 17.699 28.337 26H17.663zM23 14l-9 14h18z'/></svg>",
"folder-delta": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f8bbd0' d='M23 17.699 28.337 26H17.663zM23 14l-9 14h18z'/></svg>",
"folder-deprecated-open.clone": "<svg viewBox='0 0 1024 1024'><path fill='#616161' d='M926.912 384H302.144a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.792-358.784A64 64 0 0 0 926.912 384'/><path fill='#bdbdbd' d='M512 320c-35.456 0-64 28.544-64 64v64c0 35.456 28.544 64 64 64v320c0 35.456 28.544 64 64 64h320c35.456 0 64-28.544 64-64V512c35.456 0 64-28.544 64-64v-64c0-35.456-28.544-64-64-64zm0 64h448v64H512zm128 128h192v64H640z'/></svg>",
"folder-deprecated.clone": "<svg viewBox='0 0 1024 1024'><path fill='#616161' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#bdbdbd' d='M512 320c-35.456 0-64 28.544-64 64v64c0 35.456 28.544 64 64 64v320c0 35.456 28.544 64 64 64h320c35.456 0 64-28.544 64-64V512c35.456 0 64-28.544 64-64v-64c0-35.456-28.544-64-64-64zm0 64h448v64H512zm128 128h192v64H640z'/></svg>",
"folder-desktop-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6v2h-2v2h8v-2h-2v-2h6a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
"folder-desktop": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6v2h-2v2h8v-2h-2v-2h6a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
"folder-development-open.clone": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M18.473 30a1 1 0 0 1-.238-.028 1.137 1.137 0 0 1-.828-1.323L20.5 12.905a1.13 1.13 0 0 1 .507-.744 1.06 1.06 0 0 1 .8-.134 1.14 1.14 0 0 1 .828 1.324l-3.101 15.744a1.12 1.12 0 0 1-.504.743 1.06 1.06 0 0 1-.557.162m6.2-2h-.077a1.08 1.08 0 0 1-.762-.412 1.164 1.164 0 0 1 .113-1.548l5.319-4.967-5.296-4.623a1.165 1.165 0 0 1-.162-1.544 1.08 1.08 0 0 1 .754-.437 1.06 1.06 0 0 1 .81.258l6.244 5.455a1.156 1.156 0 0 1 .003 1.723l-6.218 5.808a1.07 1.07 0 0 1-.729.289Zm-9.31 0a1.07 1.07 0 0 1-.728-.292l-6.226-5.811a1.16 1.16 0 0 1-.01-1.692l.02-.018 6.246-5.454a1.03 1.03 0 0 1 .8-.26 1.08 1.08 0 0 1 .76.436 1.165 1.165 0 0 1-.16 1.547l-5.294 4.62 5.32 4.964a1.156 1.156 0 0 1 .112 1.548 1.07 1.07 0 0 1-.762.412Z'/></svg>",
@ -427,6 +429,8 @@
"folder-include": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 8v4h-2v-4h-4v-2h4v-4h2v4h4v2Z'/></svg>",
"folder-input-open": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M28.965 12H9.441a2 2 0 0 0-1.898 1.368L4 24V10h23.998a2 2 0 0 0-2-2H15.122a2 2 0 0 1-1.28-.464l-1.287-1.072A2 2 0 0 0 11.275 6H3.999a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h21.998l4.805-11.212A2 2 0 0 0 28.964 12'/><path fill='#b2ebf2' d='M13.98 10c-1.098 0-1.98.994-1.98 2.229V16h2v-4h16v14H14v-4h-2v3.772c0 1.234.882 2.228 1.98 2.228h16.039c1.098 0 1.98-.994 1.98-2.228V12.229C32 10.994 31.118 10 30.02 10zM22 14v4H12v2h10v4l5.12-5z'/></svg>",
"folder-input": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='m13.844 7.537-1.287-1.073A2 2 0 0 0 11.277 6H4.001A2 2 0 0 0 2 8v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.125a2 2 0 0 1-1.282-.463'/><path fill='#b2ebf2' d='M13.98 10c-1.098 0-1.98.994-1.98 2.229V16h2v-4h16v14H14v-4h-2v3.772c0 1.234.882 2.228 1.98 2.228h16.039c1.098 0 1.98-.994 1.98-2.228V12.229C32 10.994 31.118 10 30.02 10zM22 14v4H12v2h10v4l5.12-5z'/></svg>",
"folder-instructions-open.clone": "<svg viewBox='0 0 32 32'><path fill='#00b8d4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#b2ebf2' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
"folder-instructions.clone": "<svg viewBox='0 0 32 32'><path fill='#00b8d4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#b2ebf2' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
"folder-intellij-open": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='.445' x2='104.977' y1='3272.835' y2='3209.742' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='22.55' x2='117.962' y1='3121.343' y2='3204.873' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='28.608' x2='-27.937' y1='3197.064' y2='3161.75' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='27.588' x2='-27.616' y1='3117.085' y2='3162.678' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
"folder-intellij-open_light": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='.445' x2='104.977' y1='3611.926' y2='3548.833' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='22.55' x2='117.962' y1='3460.434' y2='3543.963' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='28.608' x2='-27.937' y1='3536.154' y2='3500.841' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='27.588' x2='-27.616' y1='3456.176' y2='3501.769' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#b0bec5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
"folder-intellij": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='-338.646' x2='-234.114' y1='3272.835' y2='3209.742' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='-316.541' x2='-221.129' y1='3121.343' y2='3204.873' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='-310.483' x2='-367.028' y1='3197.064' y2='3161.75' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='-311.503' x2='-366.707' y1='3117.085' y2='3162.678' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
@ -517,6 +521,8 @@
"folder-netlify": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a7ffeb' d='M22 16h-4v6h2v-4h1.5a.5.5 0 0 1 .5.5V22h2v-4a2 2 0 0 0-2-2'/><rect width='6' height='2' x='26' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='8' fill='#a7ffeb' rx='.5'/><rect width='6' height='2' x='10' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='24' fill='#a7ffeb' rx='.5'/><path fill='#a7ffeb' d='m13 12.172 1.414-1.414 2.828 2.828L15.828 15zM15.828 23l1.414 1.414-2.828 2.828L13 25.828z'/></svg>",
"folder-next-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M24 12a8 8 0 1 0 3.969 14.94L22 19v4.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5h1.232a.5.5 0 0 1 .416.223l6.736 10.103A7.993 7.993 0 0 0 24 12m4 8h-2v-3.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5Z'/></svg>",
"folder-next": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M24 12a8 8 0 1 0 3.969 14.94L22 19v4.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5h1.232a.5.5 0 0 1 .416.223l6.736 10.103A7.993 7.993 0 0 0 24 12m4 8h-2v-3.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5Z'/></svg>",
"folder-nginx-open": "<svg viewBox='0 0 32 32'><path fill='#388e3c' d='M28.966 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.806-11.212A2 2 0 0 0 28.966 12'/><path fill='#a5d6a7' d='m24 10.857-8 4.571v9.143l8 4.572 8-4.572v-9.143zM28 23.5v.5h-1.76a.5.5 0 0 1-.39-.187L22 19v5h-2v-8h1.76a.5.5 0 0 1 .39.188L26 21v-5h2z'/></svg>",
"folder-nginx": "<svg viewBox='0 0 32 32'><path fill='#388e3c' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a5d6a7' d='m24 10.857-8 4.571v9.143l8 4.572 8-4.572v-9.143zM28 24h-1.76a.5.5 0 0 1-.39-.187L22 19v5h-2v-8h1.76a.5.5 0 0 1 .39.188L26 21v-5h2z'/></svg>",
"folder-ngrx-actions-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1bee7' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
"folder-ngrx-actions.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
"folder-ngrx-effects-open.clone": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
@ -582,6 +588,8 @@
"folder-quasar": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M24.026 20A2.028 2.028 0 1 1 22 18.048 1.99 1.99 0 0 1 24.026 20m6.967-5.002a10 10 0 0 0-1.59-2.002L27.06 14.3a7.9 7.9 0 0 0-2.445-1.365 9.3 9.3 0 0 0-1.893 2.6 11.74 11.74 0 0 1 7.8 2.618l1.473-.819A9.8 9.8 0 0 0 30.993 15Zm0 10.002A9.8 9.8 0 0 0 32 22.67l-2.342-1.303a7.2 7.2 0 0 0 .005-2.72 10 10 0 0 0-3.285-.278 10.7 10.7 0 0 1 1.545 7.812l1.473.82A10 10 0 0 0 30.993 25m-8.992 5a10.8 10.8 0 0 0 2.597-.326v-2.603a7.9 7.9 0 0 0 2.451-1.357 9.1 9.1 0 0 0-1.392-2.88 11.36 11.36 0 0 1-6.255 5.196v1.64a10.8 10.8 0 0 0 2.599.33m-8.994-5a10 10 0 0 0 1.592 2.004L16.94 25.7a7.8 7.8 0 0 0 2.447 1.365 9.3 9.3 0 0 0 1.891-2.6 11.75 11.75 0 0 1-7.8-2.618l-1.471.819a9.8 9.8 0 0 0 1 2.333Zm0-10A9.8 9.8 0 0 0 12 17.33l2.343 1.303a7.2 7.2 0 0 0-.005 2.72 10 10 0 0 0 3.286.278 10.7 10.7 0 0 1-1.545-7.814l-1.475-.82a10 10 0 0 0-1.597 2.005Zm8.992-5a10.8 10.8 0 0 0-2.597.326v2.603a7.9 7.9 0 0 0-2.45 1.357 9.1 9.1 0 0 0 1.393 2.88A11.35 11.35 0 0 1 24.6 11.97v-1.64A10.8 10.8 0 0 0 22 10Z'/></svg>",
"folder-queue-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M24 16v-2h-3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2h-2v-8Zm8-2v-2h-5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h5v-2h-4V14Zm-16 2h2v8h-2z'/></svg>",
"folder-queue": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M24 16v-2h-3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2h-2v-8Zm8-2v-2h-5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h5v-2h-4V14Zm-16 2h2v8h-2z'/></svg>",
"folder-r-open": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#90caf9' fill-rule='evenodd' d='M16 22.405v2.786c-4.8-1.097-8.263-4.17-8.263-7.804 0-4.553 5.432-8.245 12.132-8.245S32 12.834 32 17.387a6.46 6.46 0 0 1-1.915 4.433l-.49-.855a4.65 4.65 0 0 0 .981-2.962c0-3.91-3.758-5.637-8.85-5.637-5.093 0-9.221 2.523-9.221 5.637 0 1.787 1.372 3.37 3.495 4.402m15.332 5.596V28h-.001Zm0-.001h-4.656l-2.177-4.208a5.4 5.4 0 0 0-.72-1.017c-.23-.19-.327-.258-.553-.258H22V28h-4V14h8.274S30 14.07 30 17.76a3.866 3.866 0 0 1-3.559 3.964 9 9 0 0 1 1.156.448 2.2 2.2 0 0 1 .586.431 1.6 1.6 0 0 1 .268.396ZM26 17.5a1.5 1.5 0 0 0-1.5-1.5H22v4h2.5a1.5 1.5 0 0 0 1.5-1.5Z'/></svg>",
"folder-r": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#90caf9' fill-rule='evenodd' d='M16 22.405v2.786c-4.8-1.097-8.263-4.17-8.263-7.804 0-4.553 5.432-8.245 12.132-8.245S32 12.834 32 17.387a6.46 6.46 0 0 1-1.915 4.433l-.49-.855a4.65 4.65 0 0 0 .981-2.962c0-3.91-3.758-5.637-8.85-5.637-5.093 0-9.221 2.523-9.221 5.637 0 1.787 1.372 3.37 3.495 4.402m15.332 5.596V28h-.001Zm0-.001h-4.656l-2.177-4.208a5.4 5.4 0 0 0-.72-1.017c-.23-.19-.327-.258-.553-.258H22V28h-4V14h8.274S30 14.07 30 17.76a3.866 3.866 0 0 1-3.559 3.964 9 9 0 0 1 1.156.448 2.2 2.2 0 0 1 .586.431 1.6 1.6 0 0 1 .268.396ZM26 17.5a1.5 1.5 0 0 0-1.5-1.5H22v4h2.5a1.5 1.5 0 0 0 1.5-1.5Z'/></svg>",
"folder-react-components-open": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' d='M14.484 6H4.72a1 1 0 0 0-.949.684L2 12V5h13a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232l-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h11l2.403-5.606A1 1 0 0 0 14.483 6'/><g fill='#b2ebf2'><path d='M10.5 8.399c2.924 0 4.714 1.037 4.714 1.6s-1.79 1.602-4.714 1.602S5.785 10.564 5.785 10s1.79-1.601 4.715-1.601m0-.8c-3.038 0-5.5 1.075-5.5 2.4s2.462 2.402 5.5 2.402S16 11.326 16 10s-2.463-2.401-5.5-2.401'/><path d='M10.5 9.2a.786.8 0 1 0 .785.8.786.8 0 0 0-.785-.8'/><path d='M8.322 5.8c.793 0 2.333 1.272 3.538 3.4 1.463 2.58 1.476 4.677.997 4.959a.354.36 0 0 1-.18.04c-.792 0-2.333-1.271-3.538-3.399-1.463-2.58-1.476-4.677-.997-4.96a.354.36 0 0 1 .18-.04m0-.8a1.128 1.149 0 0 0-.572.147c-1.128.663-.81 3.374.708 6.054C9.748 13.478 11.491 15 12.678 15a1.128 1.149 0 0 0 .572-.148c1.127-.663.81-3.373-.71-6.053C11.25 6.522 9.509 5 8.323 5Z'/><path d='M12.677 5.8a.354.36 0 0 1 .18.04c.48.283.466 2.38-.997 4.96-1.206 2.128-2.746 3.4-3.538 3.4a.354.36 0 0 1-.18-.04c-.48-.284-.466-2.38.997-4.96 1.206-2.128 2.746-3.4 3.538-3.4m0-.8c-1.186 0-2.929 1.522-4.22 3.8-1.517 2.68-1.835 5.39-.707 6.052a1.128 1.149 0 0 0 .572.148c1.186 0 2.929-1.523 4.22-3.8 1.517-2.68 1.835-5.39.708-6.052A1.128 1.149 0 0 0 12.677 5'/></g></svg>",
"folder-react-components": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/><g fill='#b2ebf2'><path d='M10.5 8.399c2.924 0 4.714 1.037 4.714 1.6s-1.79 1.602-4.714 1.602S5.785 10.564 5.785 10s1.79-1.601 4.715-1.601m0-.8c-3.038 0-5.5 1.075-5.5 2.4s2.462 2.402 5.5 2.402S16 11.326 16 10s-2.463-2.401-5.5-2.401'/><path d='M10.5 9.2a.786.8 0 1 0 .785.8.786.8 0 0 0-.785-.8'/><path d='M8.322 5.8c.793 0 2.333 1.272 3.538 3.4 1.463 2.58 1.476 4.677.997 4.959a.354.36 0 0 1-.18.04c-.792 0-2.333-1.271-3.538-3.399-1.463-2.58-1.476-4.677-.997-4.96a.354.36 0 0 1 .18-.04m0-.8a1.128 1.149 0 0 0-.572.147c-1.128.663-.81 3.374.708 6.054C9.748 13.478 11.491 15 12.678 15a1.128 1.149 0 0 0 .572-.148c1.127-.663.81-3.373-.71-6.053C11.25 6.522 9.509 5 8.323 5Z'/><path d='M12.677 5.8a.354.36 0 0 1 .18.04c.48.283.466 2.38-.997 4.96-1.206 2.128-2.746 3.4-3.538 3.4a.354.36 0 0 1-.18-.04c-.48-.284-.466-2.38.997-4.96 1.206-2.128 2.746-3.4 3.538-3.4m0-.8c-1.186 0-2.929 1.522-4.22 3.8-1.517 2.68-1.835 5.39-.707 6.052a1.128 1.149 0 0 0 .572.148c1.186 0 2.929-1.523 4.22-3.8 1.517-2.68 1.835-5.39.708-6.052A1.128 1.149 0 0 0 12.677 5'/></g></svg>",
"folder-redux-actions-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
@ -622,6 +630,8 @@
"folder-scala": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M18 26v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2'/></svg>",
"folder-scons-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M12 24h8v4h-8Zm12 0h8v4h-8Zm-12-6h4v4h-4Zm16 0h4v4h-4Zm-10-8h8v4h-8Zm4 14-4-4h2v-4h4v4h2Z'/></svg>",
"folder-scons": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#ffcdd2' d='M12 24h8v4h-8Zm12 0h8v4h-8Zm-12-6h4v4h-4Zm16 0h4v4h-4Zm-10-8h8v4h-8Zm4 14-4-4h2v-4h4v4h2Z'/></svg>",
"folder-scrap-open.clone": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#616161' d='M463.47 192H151.06c-13.77 0-26 8.82-30.35 21.89L64 384V160h384c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42l-20.6-17.15c-5.75-4.8-13-7.43-20.48-7.43H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h352l76.88-179.39c1.7-3.98 2.59-8.28 2.59-12.61 0-17.67-14.33-32-32-32'/><path fill='#bdbdbd' d='M320 160v32h-96v32h32v192c0 17.63 14.38 32 32 32h160c17.63 0 32-14.37 32-32V224h32v-32h-96v-32zm0 96v128h32V256zm64 0v128h32V256z'/></svg>",
"folder-scrap.clone": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#616161' d='m221.5 120.58-20.6-17.16A32 32 0 0 0 180.42 96H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32V160c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42'/><path fill='#bdbdbd' d='M320 160v32h-96v32h32v192c0 17.63 14.38 32 32 32h160c17.63 0 32-14.37 32-32V224h32v-32h-96v-32zm0 96v128h32V256zm64 0v128h32V256z'/></svg>",
"folder-scripts-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M28 12h-6a4 4 0 0 0-4 4v8h2v-8h8v9.893a2.074 2.074 0 0 1-1.664 2.08A2 2 0 0 1 24 26h-8a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V16h2a4 4 0 0 0-4-4'/></svg>",
"folder-scripts": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M28 12h-6a4 4 0 0 0-4 4v8h2v-8h8v9.893a2.074 2.074 0 0 1-1.664 2.08A2 2 0 0 1 24 26h-8a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V16h2a4 4 0 0 0-4-4'/></svg>",
"folder-secure-open": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M28 16v-3.828a4.116 4.116 0 0 0-3.607-4.153A4 4 0 0 0 20 12v4h-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2Zm-4 8a2 2 0 1 1 2-2 2 2 0 0 1-2 2m2-8h-4v-4a2 2 0 0 1 4 0Z'/></svg>",
@ -638,6 +648,8 @@
"folder-shared": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M28 26a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h-4v2h18v-2Zm-5-6v-2l4 2.798L23 24v-2a4.12 4.12 0 0 0-4 2c.448-2.003.888-3.595 4-4'/></svg>",
"folder-simulations-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.965 12H9.441a2 2 0 0 0-1.898 1.368L4 24V10h23.998a2 2 0 0 0-2-2H15.122a2 2 0 0 1-1.28-.464l-1.287-1.072A2 2 0 0 0 11.275 6H3.999a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h21.998l4.805-11.212A2 2 0 0 0 28.964 12'/><path fill='#e1bee7' d='m24.029 28.172-1.474-3.447c1.417-.523 2.733-1.225 3.97-2.044zm-6.749-8.723-3.448-1.466 5.501-2.493a18.6 18.6 0 0 0-2.053 3.96m14.383-9.1s-4.458-1.907-9.55 3.187c-1.974 1.97-3.154 4.14-3.914 6.04a1.79 1.79 0 0 0 .409 1.916l1.916 1.908c.5.504 1.237.666 1.906.414a17.2 17.2 0 0 0 6.046-3.914c5.093-5.095 3.187-9.55 3.187-9.55m-6.374 6.363a1.82 1.82 0 0 1 0-2.547 1.81 1.81 0 0 1 2.552 0 1.81 1.81 0 0 1 0 2.547 1.81 1.81 0 0 1-2.552 0m-5.093 6.364-1.26-1.27zm-2.371 4.923 3.278-3.276a2.8 2.8 0 0 1-.873-.406l-3.675 3.682zm-3.811 0h1.259l4.299-4.284-1.282-1.27-4.276 4.285zm0-2.548 3.675-3.671a2.6 2.6 0 0 1-.408-.874l-3.267 3.277z'/></svg>",
"folder-simulations": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.843 7.537-1.287-1.073A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h23.998A2 2 0 0 0 30 24V10a2 2 0 0 0-2-2.001H15.122a2 2 0 0 1-1.28-.463'/><path fill='#e1bee7' d='m24.029 28.172-1.474-3.447c1.417-.523 2.733-1.225 3.97-2.044zm-6.749-8.723-3.448-1.466 5.501-2.493a18.6 18.6 0 0 0-2.053 3.96m14.383-9.1s-4.458-1.907-9.55 3.187c-1.974 1.97-3.154 4.14-3.914 6.04a1.79 1.79 0 0 0 .409 1.916l1.916 1.908c.5.504 1.237.666 1.906.414a17.2 17.2 0 0 0 6.046-3.914c5.093-5.095 3.187-9.55 3.187-9.55m-6.374 6.363a1.82 1.82 0 0 1 0-2.547 1.81 1.81 0 0 1 2.552 0 1.81 1.81 0 0 1 0 2.547 1.81 1.81 0 0 1-2.552 0m-5.093 6.364-1.26-1.27zm-2.371 4.923 3.278-3.276a2.8 2.8 0 0 1-.873-.406l-3.675 3.682zm-3.811 0h1.259l4.299-4.284-1.282-1.27-4.276 4.285zm0-2.548 3.675-3.671a2.6 2.6 0 0 1-.408-.874l-3.267 3.277z'/></svg>",
"folder-skills-open": "<svg viewBox='0 0 32 32'><path fill='#ff8f00' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='M24 26v2h4v-2zm0-16-4 4v4l2 2v2l2 2h4l2-2v-2l2-2v-4l-4-4-2 2v2l-2 2h-2v-2l2-2h2l2-2z'/></svg>",
"folder-skills": "<svg viewBox='0 0 32 32'><path fill='#ff8f00' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='M24 26v2h4v-2zm0-16-4 4v4l2 2v2l2 2h4l2-2v-2l2-2v-4l-4-4-2 2v2l-2 2h-2v-2l2-2h2l2-2z'/></svg>",
"folder-snapcraft-open": "<svg viewBox='0 0 16 16'><path fill='#66bb6a' d='M14.033 6H4.597a.97.97 0 0 0-.918.684L2 12V5h11.566c0-.552-.433-1-.967-1H7.343a.95.95 0 0 1-.619-.232l-.622-.536A.95.95 0 0 0 5.483 3H1.967C1.433 3 1 3.448 1 4v8c0 .552.433 1 .967 1h10.632l2.323-5.606c.273-.66-.195-1.394-.889-1.394'/><path fill='#dcedc8' d='m12.538 7.077 2.077 1.038-2.077 2.077zM8.385 14l3.807-3.462-1.73-1.73zM7 5l5.192 5.192V7.077zm8.654 2.077-3.116-.346L16 8.46z'/></svg>",
"folder-snapcraft": "<svg viewBox='0 0 16 16'><path fill='#66bb6a' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/><path fill='#dcedc8' d='m12.538 7.077 2.077 1.038-2.077 2.077zM8.385 14l3.807-3.462-1.73-1.73zM7 5l5.192 5.192V7.077zm8.654 2.077-3.116-.346L16 8.46z'/></svg>",
"folder-snippet-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M29 12H9.4a2 2 0 0 0-1.9 1.4L4 24V10h24a2 2 0 0 0-2-2H15.1a2 2 0 0 1-1.3-.5l-1.2-1a2 2 0 0 0-1.3-.5H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.8-11.2A2 2 0 0 0 29 12'/><path fill='#ffe0b2' d='M20 10s-2 1.86-2 4 6 8 6 8l-.89.88a3 3 0 0 0-5.1 2A3.01 3.01 0 0 0 20.87 28a3.02 3.02 0 0 0 3.11-3.24L26 23.5l.25.31A3.02 3.02 0 0 0 28.88 28 3.01 3.01 0 0 0 32 25.12a3.01 3.01 0 0 0-4.38-2.78zm10 0-4 8 2 2s4-3.94 4-6-2-4-2-4m-9.06 14h.1c.51.02.9.4.95.89v.2a.98.98 0 0 1-1.03.91.99.99 0 0 1-.96-1.04.98.98 0 0 1 .94-.96m8 0h.1c.56.02.98.48.96 1.04a.98.98 0 0 1-1.04.96.98.98 0 0 1-.96-1.04.98.98 0 0 1 .94-.96' style='-inkscape-stroke:none'/></svg>",
@ -808,6 +820,7 @@
"image": "<svg viewBox='0 0 16 16'><path fill='#26a69a' d='M8.5 6h4l-4-4zM3.875 1H9.5l4 4v8.6c0 .773-.616 1.4-1.375 1.4h-8.25c-.76 0-1.375-.627-1.375-1.4V2.4c0-.777.612-1.4 1.375-1.4M4 13.6h8V8l-2.625 2.8L8 9.4zm1.25-7.7c-.76 0-1.375.627-1.375 1.4s.616 1.4 1.375 1.4c.76 0 1.375-.627 1.375-1.4S6.009 5.9 5.25 5.9'/></svg>",
"imba": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 201 201'><path fill='#ffc400' d='M161.96 61.952c-3.043 13.905-32.633 79.576-36.431 94.457-2.698 10.575 11.229 23.851 13.555 15.159 6.84-25.548 37.32-86.251 39.023-98.893 1.468-10.897-14.66-17.516-16.147-10.723m-37.128 48.192a4.97 4.97 0 0 1 5.726 6.91c.023.012.021.015.021.016a19.04 19.04 0 0 1-13.667 10.676c-3.4.645-7.236 1.182-11.504 1.588-15.316 1.453-31.743-17.007-20.624-16.49 16.552.77 29.747-.447 40.047-2.7zm16.256-17.347a13.36 13.36 0 0 1-9.677 8.152c-20.232 4.242-49.32 2.59-63.662-.888-13.94-3.38-23.102-23.665-14.05-20.64 21.019 7.024 60.118 9.347 82.248 6.838a4.808 4.808 0 0 1 5.133 6.523c.011.004.011.004.008.015m8.398-23.8a11.39 11.39 0 0 1-9.973 8.037c-40.633 2.924-92.83-6.466-107.91-22.019C20.273 43.326 21 20.85 27.442 27.992c24.417 27.072 84.437 34.865 117.12 34.521a5.022 5.022 0 0 1 4.92 6.481q.003.002.001.003z'/></svg>",
"installation": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='M12 7h-2V2H6v5H4l4 4zm-9 5.5V14h10v-1.5z'/></svg>",
"instructions.clone": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#00b8d4' d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2m0 14H8V4h12zM10 9h8v2h-8zm0 3h4v2h-4zm0-6h8v2h-8z'/></svg>",
"ionic": "<svg viewBox='0 0 512 512'><g fill='#448aff'><path d='M423.59 132.8a31.86 31.86 0 0 0 5.408-17.804c0-17.675-14.33-32-32-32a31.85 31.85 0 0 0-17.805 5.409c-34.486-25.394-77.085-40.409-123.2-40.409-114.88 0-208 93.125-208 208 0 114.88 93.125 208 208 208 114.87 0 208-93.123 208-208 0-46.111-15.016-88.71-40.408-123.2zm-31.762 259.03c-17.646 17.646-38.191 31.499-61.064 41.174-23.672 10.012-48.826 15.089-74.766 15.089s-51.095-5.077-74.767-15.089c-22.873-9.675-43.417-23.527-61.064-41.174s-31.5-38.191-41.174-61.064c-10.013-23.672-15.09-48.828-15.09-74.768s5.077-51.095 15.089-74.767c9.674-22.873 23.527-43.417 41.174-61.064s38.191-31.5 61.064-41.174c23.673-10.013 48.828-15.09 74.768-15.09s51.094 5.077 74.766 15.089a191.2 191.2 0 0 1 37.802 21.327 31.85 31.85 0 0 0-3.568 14.679c0 17.675 14.327 32 32 32 5.293 0 10.28-1.293 14.678-3.568a191 191 0 0 1 21.327 37.801c10.013 23.672 15.09 48.827 15.09 74.767s-5.077 51.096-15.09 74.768c-9.675 22.873-23.527 43.418-41.175 61.064'/><circle cx='256' cy='256' r='96'/></g></svg>",
"istanbul": "<svg viewBox='0 0 32 32'><path fill='#fdd835' d='M30 13v-3h-2v3h-1v.5a.5.5 0 0 0 .5.5h.5v10h-1v-2h-1v-8h-1v6h-2v-3h-1v1h-1v-1h1a6.35 6.35 0 0 0-6-4 6.35 6.35 0 0 0-6 4h1v1h-1v-1H9v3H7v-6H6v8H5v2H4V14h.5a.5.5 0 0 0 .5-.5V13H4v-3H2v3H1v.5a.5.5 0 0 0 .5.5H2v12h8v-6h2v6h8v-6h2v6h8V14h.5a.5.5 0 0 0 .5-.5V13ZM14 24h-1v-2h1Zm3 0h-2v-2a1 1 0 0 1 2 0Zm2 0h-1v-2h1Zm-1-7v1h-1v-1Zm-3 0v1h-1v-1Zm-3 0h1v1h-1Zm7 1v-1h1v1ZM3 6 2 9h2zm26 0-1 3h2z'/><path fill='#fdd835' d='m16 10-1 2h2zm9.5 1-.5 2h1zm-19 0L6 13h1z'/></svg>",
"jar": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M22 10h2v4h-2z'/><path fill='#f44336' d='M28 2H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2m-2 12a2 2 0 0 1-2 2h-2v4a4 4 0 0 1-4 4h-8a4 4 0 0 1-4-4V8h18a2 2 0 0 1 2 2Z'/></svg>",
@ -821,6 +834,7 @@
"jinja_light": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M30 8V4a94.4 94.4 0 0 1-14 1A94.4 94.4 0 0 1 2 4v4s1.482.247 3.95.495L5.4 14H2v2h3.2L4 28h6V16h12v12h6l-1.2-12H30v-2h-3.4l-.55-5.505C28.517 8.247 30 8 30 8m-20 6V8.817c1.235.074 2.576.13 4 .16V14Zm12 0h-4V8.977a104 104 0 0 0 4-.16Z'/></svg>",
"jsconfig": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z' data-mit-no-recolor='true'/><path fill='#ffca28' d='M12 12v18h18V12zm8 12a2.006 2.006 0 0 1-2 2h-2a2.006 2.006 0 0 1-2-2v-2h2v2h2v-8h2zm8-6h-4v2h2a2.006 2.006 0 0 1 2 2v2a2.006 2.006 0 0 1-2 2h-4v-2h4v-2h-2a2.006 2.006 0 0 1-2-2v-2a2.006 2.006 0 0 1 2-2h4z'/></svg>",
"json": "<svg viewBox='0 -960 960 960'><path fill='#f9a825' d='M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80z'/></svg>",
"json_schema": "<svg viewBox='0 -960 960 960'><path fill='#f9a825' d='M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80z'/><path fill='#f9a825' stroke='#f9a825' stroke-width='36.192' d='M581.79-570.48 446.07-434.76l-67.859-67.859-22.62 22.62 90.479 90.479 158.34-158.34z'/></svg>",
"jsr": "<svg viewBox='0 0 16 16'><path fill='#fdd835' d='M2 7h1v2h1V5h1v5H2m4-5h4v1H7v1.5h3V11H6v-1h3V8.5H6M11 6h3v2.5h-1V7h-1v4h-1'/></svg>",
"jsr_light": "<svg viewBox='0 0 16 16'><path fill='#37474f' d='M1 6h2V4h8v1h4v5h-2v2H5v-1H1'/><path fill='#fdd835' d='M2 7h1v2h1V5h1v5H2m4-5h4v1H7v1.5h3V11H6v-1h3V8.5H6M11 6h3v2.5h-1V7h-1v4h-1'/></svg>",
"julia": "<svg viewBox='0 0 50 50'><g transform='translate(.21 -247.01)'><circle cx='13.497' cy='281.63' r='9.555' fill='#c62828'/><circle cx='36.081' cy='281.63' r='9.555' fill='#7e57c2'/><circle cx='24.722' cy='262.39' r='9.555' fill='#388e3c'/></g></svg>",
@ -1024,6 +1038,7 @@
"robots": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.586 18H28a8 8 0 0 0-8-8h-2V8.445a4 4 0 1 0-4 0V10h-2a8 8 0 0 0-8 8h-.586A1.414 1.414 0 0 0 2 19.414v3.172A1.414 1.414 0 0 0 3.414 24H4v1a3 3 0 0 0 3 3h18a3 3 0 0 0 3-3v-1h.586A1.414 1.414 0 0 0 30 22.586v-3.172A1.414 1.414 0 0 0 28.586 18M11 22a3 3 0 1 1 3-3 3 3 0 0 1-3 3m10 0a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
"rocket": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M12 26c-1.71 1.905-7.64 2.149-7.93 1.937l-.004.003-.002-.005-.005-.002.004-.003C3.85 27.64 4.1 21.715 6 20ZM29.64 7.908c-.655 2.69-1.681 5.64-5.64 10.092l-.789 4.749a4 4 0 0 1-1.116 2.171l-4.863 4.868A.72.72 0 0 1 16 29.277v-6.23L9 16H2.723a.721.721 0 0 1-.511-1.232L7.08 9.905A4 4 0 0 1 9.25 8.79L14 8c4.453-3.959 7.402-4.985 10.092-5.64a11.1 11.1 0 0 1 3.642-.329 3.1 3.1 0 0 1 1.72.515 3.1 3.1 0 0 1 .515 1.72 11.1 11.1 0 0 1-.33 3.642ZM26 10a4 4 0 1 0-4 4 4 4 0 0 0 4-4'/></svg>",
"rojo": "<svg fill='none' viewBox='0 0 24 24'><path fill='#e53935' d='M9.586 2h.077l-.077.135v.29c.598-.116.908-.27.908-.425l.425.058A36 36 0 0 1 12.869 2h.077c.927 0 2.009.502 3.284 1.526.27.444.424.83.502 1.197v.695c-.155.966-.425 1.7-.773 2.183q-.319.26-1.39 1.738h-.078v-.135l.194-.29h-.116a17.6 17.6 0 0 1-5.601 3.226 6.2 6.2 0 0 1-1.545.212c.656.772 1.873 1.796 3.63 3.07.174.097 1.295.812 3.361 2.183 1.719 1.062 3.766 2.221 6.161 3.496v.077h-.29c-.463-.174-.695-.309-.695-.367l-.135.077h-.135c-.058 0-.078-.019-.078-.077-.173.039-.309.213-.424.502.29.078.424.174.424.27-.115.097-.193.155-.27.155l-.348-.077v.077c0 .078.058.135.194.135v.213h-.058c-.097 0-1.16-.618-3.148-1.835-1.41-.715-3.149-1.815-5.176-3.283-.676-.31-1.912-1.217-3.728-2.723h-.193c-.27.405-.618 1.41-1.062 3.013-.097.367-.56.907-1.39 1.602h-.155l-.135-.212v-.077c0-.097.077-.31.212-.618v-.077h-.077c0 .135-.29.347-.83.637H3v-.135c1.642-4.867 2.781-8.035 3.438-9.522q1.68-3.852 1.68-4.403c-.251 0-.811.309-1.68.965h-.135l.058-.135v-.135c-.252 0-.831.406-1.739 1.255h-.077v-.077l.908-1.043.154-.212v-.077h-.154l-.83.695c-.097 0-.136-.039-.136-.116A11.1 11.1 0 0 1 7.693 3.12c1.178-.309 1.758-.656 1.758-1.062zm6.373 1.95v.078c0 .077.058.135.135.135v-.077c0-.077-.038-.135-.135-.135M7.77 10.673h.058c.85-.174 1.275-.31 1.275-.425-.058-.097-.077-.155-.077-.213C10.803 9.436 12 8.837 12.599 8.22c.058 0 .425-.386 1.12-1.198h.077V7.1c-.096.154-.154.27-.154.347h.077c.599-.463.908-.965.908-1.525v-.425c-.136 0-.213-.058-.213-.155q.348-.057.348-.347 0-.348-1.39-.696c-.754-.193-1.565-.27-2.453-.27-.445 0-.889.811-1.333 2.453-.193.193-.792 1.583-1.816 4.19m.83-5.949V4.8c.078 0 .136-.077.213-.212v-.077c-.058 0-.135.077-.212.212m.078 1.333h.077c.136-.058.213-.193.213-.425-.077 0-.174.135-.29.425m6.586.27v.155c.058 0 .135-.078.193-.213v-.077H15.4c-.096.02-.135.058-.135.135m-7.57 4.693v.078h.134a.5.5 0 0 1 .213-.078v.078c1.14-.213 1.815-.445 2.047-.696h-.077c-1.024.232-1.796.444-2.318.618m-.271.696h.058l1.274-.27-.077-.136c-.83.097-1.255.251-1.255.406m2.78 4.345v.135c.735.56 1.179.85 1.334.85-.174-.135-.27-.251-.27-.348zm2.048 1.545c.039.174.174.27.406.27h.077v-.077c-.116 0-.251-.058-.425-.193zm4.539 2.51q.55.495 1.68.986l.077-.135c-.58-.348-1.12-.638-1.603-.85zm3.862 1.121h.213c.077.02.135.058.135.135-.097 0-.135.078-.135.213h-.155l.078-.136v-.077h-.136z'/></svg>",
"rolldown": "<svg fill='none' viewBox='0 0 16 16'><path fill='#f4511e' d='M13.628 1c.3 0 .465.35.272.582l-3.277 3.933c-.385.462-.229 1.485.374 1.485h3.644c.301 0 .465.35.273.582l-6.645 7.29a.355.355 0 0 1-.545 0l-6.64-7.29A.355.355 0 0 1 1.355 7h3.64c.602 0 .759-1.023.373-1.485L2.092 1.582A.355.355 0 0 1 2.364 1z'/></svg>",
"rollup": "<svg viewBox='100 100 800 800'><path fill='#f44336' d='M733.79 394.71c0 77.407-42.308 144.79-104.67 180.51-3.76 3.134-5.954 8.148-3.76 12.849l106.87 211.22c2.82 6.581-1.568 14.103-8.776 14.103h-408.35l2.194-1.254c15.356-8.774 121.91-219.06 225.95-318.72 104.05-99.658 117.21-66.439 59.857-174.87 0 0 44.188 86.182 6.581 92.763-29.459 5.328-97.15-60.17-72.08-119.09 25.071-57.664 123.79-46.695 169.23.314 17.236 30.085 26.952 64.872 26.952 102.17m-385.47 140.71c-41.367 76.154-67.692 131.62-82.108 170.48v-509.57c0-5.328 4.388-9.715 9.715-9.715h252.91c73.333 1.253 137.58 40.114 173.62 98.718-26.325-32.906-67.692-51.71-108.43-51.71-77.407 0-96.837 28.206-245.7 301.79z'/></svg>",
"rome": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M9.875 5.409A14.02 14.02 0 0 0 2.01 17.48a.51.51 0 0 0 .508.519H5.52a.495.495 0 0 0 .491-.481 10.01 10.01 0 0 1 5.273-8.337.494.494 0 0 0 .243-.592l-.958-2.882a.505.505 0 0 0-.694-.3Zm11.556.299-.958 2.882a.494.494 0 0 0 .243.592 10.01 10.01 0 0 1 5.273 8.337.495.495 0 0 0 .49.481h3.004a.51.51 0 0 0 .508-.519A14.02 14.02 0 0 0 22.125 5.41a.505.505 0 0 0-.694.299ZM26 20.5v9a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5m-24 0v9a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5'/><path fill='#ffc400' d='M16.13 10h-.26A7.87 7.87 0 0 0 8 17.87V29.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V17.87A3.88 3.88 0 0 1 15.87 14h.26A3.88 3.88 0 0 1 20 17.87V29.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V17.87A7.87 7.87 0 0 0 16.13 10m1.51-2h-3.28a.5.5 0 0 1-.474-.342l-1.667-5A.5.5 0 0 1 12.694 2h6.612a.5.5 0 0 1 .475.658l-1.667 5A.5.5 0 0 1 17.64 8'/></svg>",
"routing": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M18 14v-2h8l2-3-2-3h-8V4l-2-2-2 2v6H6l-2 3 2 3h8v10a4 4 0 0 0-4 4h12a4 4 0 0 0-4-4v-6h8l2-3-2-3Z'/></svg>",
@ -1060,6 +1075,7 @@
"simulink": "<svg viewBox='0 0 4.233 4.233'><path fill='#9e9e9e' d='M.53 1.323v.264h2.38v.794h.265V1.323z'/><path fill='#ff6e40' d='M2.381 2.381h1.323v1.323H2.381z'/><path fill='#64b5f6' d='m2.381 1.455-1.587.926V.53z'/></svg>",
"siyuan": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M2 11.976 10 4v16l-8 8Z'/><path fill='#455a64' d='M30 11.976 22 4v15.99L30 28ZM10 4l6 6v16l-6-6Z'/><path fill='#e53935' d='m22 4-6 6v16l6-6.01Z'/></svg>",
"sketch": "<svg viewBox='0 0 24 24'><path fill='#ffc107' d='M15.705 9.221h2.779l-4.632 6.484m-3.705-6.484h3.705L12 16.631m-6.484-7.41h2.779l1.852 6.484M14.78 4.59h1.852l1.853 2.779h-2.78m-4.63-2.779h1.852l.926 2.779h-3.705M7.368 4.59h1.853l-.926 2.78h-2.78m.927-4.631L2.737 8.294 12 21.263l9.262-12.968-3.705-5.557z'/></svg>",
"skill": "<svg viewBox='0 0 16 16'><path fill='#ff8f00' d='M6 12v1h4v-1zm1 2v1h2v-1zM7 1 5 2 4 4v2l1 2 1 1v1l1 1h2l1-1V9l1-1 1-2V4l-1-2-2-1v2L7 5H6V4l2-2h1v1-2z'/></svg>",
"slim": "<svg viewBox='0 0 32 32'><path fill='#f57f17' d='M23 2H9a7 7 0 0 0-7 7v14a7 7 0 0 0 7 7h14a7 7 0 0 0 7-7V9a7 7 0 0 0-7-7m-5 14-4-6v6H6a10 10 0 0 1 20 0Z'/></svg>",
"slint": "<svg viewBox='0 0 16 16'><path fill='#2979ff' d='M12 1 3 7l5 2-2-2Z'/><path fill='#2979ff' d='m4 15 9-6-5-2 2 2Z'/></svg>",
"slug": "<svg viewBox='0 0 24 24'><path fill='#f9a825' d='m9.164 21.221-.983-.056c-3.402-.19-5.654-.714-6.125-1.427-.136-.205-.146-.515-.022-.681.115-.154.377-.28.744-.355l.313-.064.373.122c1.568.517 2.903.589 4.875.263.82-.135 1.26-.29 1.736-.613.183-.124.26-.152.413-.152.62-.002 1.581-.168 2.066-.357 1.392-.544 2.655-2.023 2.979-3.49.159-.717.072-1.83-.211-2.693l-.13-.397.028-.747c.023-.606.047-.818.126-1.123.29-1.108.991-1.878 1.957-2.145.374-.103 1.17-.093 1.589.02a3.34 3.34 0 0 1 1.595.941c.505.548.707 1.025.735 1.738.021.55-.034.809-.283 1.316-.211.43-.542.833-.909 1.107-.15.113-.302.23-.336.26-.052.046-.067.192-.087.837-.014.43-.053.986-.086 1.235-.458 3.354-2.234 5.421-5.307 6.174-1.023.25-1.37.283-3.183.292-.908.005-1.748.002-1.867-.005m-3.967-2.877-.45-.07-.3-.322c-1.514-1.613-2.085-4.002-1.43-5.98.646-1.953 2.32-3.39 4.496-3.86.557-.121 1.912-.133 2.437-.023a6.7 6.7 0 0 1 1.631.56c1.828.904 2.982 2.495 3.205 4.421.111.955-.13 1.793-.763 2.651-.712.966-1.618 1.525-2.681 1.654l-.233.028.267-.277c.475-.495.596-.93.596-2.15 0-.683-.011-.831-.087-1.134-.182-.721-.476-1.239-.967-1.705-.643-.611-1.44-.923-2.349-.92-1.295.005-2.196.828-2.196 2.006 0 .398.069.695.24 1.033.135.269.446.62.68.769.368.233.984.35 1.195.225a.387.387 0 0 0 .096-.61c-.09-.101-.15-.126-.364-.154-.337-.043-.437-.086-.642-.275-.26-.239-.395-.57-.397-.964-.002-.269.014-.348.11-.543.133-.27.396-.497.703-.603.327-.114.98-.096 1.386.038.854.281 1.456.918 1.699 1.797.054.196.07.401.07.903 0 .57-.011.683-.092.941-.103.327-.31.758-.488 1.019-.295.429-.853.927-1.283 1.144-.691.348-2.98.573-4.09.4zM19.199 6.578l-.244-.078.14-.4c.537-1.544 1.334-2.521 2.057-2.521.567 0 1.043.588.862 1.067-.045.12-.244.345-.37.419-.044.025-.31.054-.597.065-.497.019-.524.024-.688.139-.221.154-.503.575-.69 1.03-.081.2-.166.361-.188.36a3 3 0 0 1-.282-.081m-2.185-.06c-.068-.115-.127-.837-.126-1.545.001-.639.015-.863.07-1.08.194-.765.529-1.121 1.055-1.121.588 0 .932.42.782.957-.072.258-.183.365-.591.568-.312.155-.394.216-.496.369-.176.262-.228.59-.2 1.253l.023.538-.165.027c-.09.014-.202.035-.249.047s-.092.006-.103-.012z'/></svg>",

View File

@ -213,6 +213,9 @@
"editor.buttons.switch_to_legacy.tooltip": "Use the legacy editor instead",
"editor.buttons.enable_monospace_font": "Enable monospace font",
"editor.buttons.disable_monospace_font": "Disable monospace font",
"editor.code_editor.command_palette": "Command Palette",
"editor.code_editor.find": "Find",
"editor.code_editor.placeholder": "Enter file content here",
"filter.string.asc": "AZ",
"filter.string.desc": "ZA",
"error.occurred": "An error occurred",
@ -2250,13 +2253,14 @@
"repo.settings.webhook.delivery.success": "An event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history.",
"repo.settings.githooks_desc": "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations.",
"repo.settings.githook_edit_desc": "If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.",
"repo.settings.githook_name": "Hook Name",
"repo.settings.githook_content": "Hook Content",
"repo.settings.update_githook": "Update Hook",
"repo.settings.add_webhook_desc": "Gitea will send <code>POST</code> requests with a specified content type to the target URL. Read more in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">webhooks guide</a>.",
"repo.settings.payload_url": "Target URL",
"repo.settings.http_method": "HTTP Method",
"repo.settings.content_type": "POST Content Type",
"repo.settings.webhook.name": "Webhook name",
"repo.settings.webhook.name_helper": "Optionally give this webhook a friendly name",
"repo.settings.webhook.name_empty": "Unnamed Webhook",
"repo.settings.secret": "Secret",
"repo.settings.webhook_secret_desc": "If the webhook server supports using secret, you can follow the webhook's manual and fill in a secret here.",
"repo.settings.slack_username": "Username",
@ -2778,9 +2782,9 @@
"org.settings.labels_desc": "Add labels which can be used on issues for <strong>all repositories</strong> under this organization.",
"org.members.membership_visibility": "Membership Visibility:",
"org.members.public": "Visible",
"org.members.public_helper": "make hidden",
"org.members.public_helper": "Make hidden",
"org.members.private": "Hidden",
"org.members.private_helper": "make visible",
"org.members.private_helper": "Make visible",
"org.members.member_role": "Member Role:",
"org.members.owner": "Owner",
"org.members.member": "Member",
@ -2808,7 +2812,10 @@
"org.teams.no_desc": "This team has no description",
"org.teams.settings": "Settings",
"org.teams.owners_permission_desc": "Owners have full access to <strong>all repositories</strong> and have <strong>administrator access</strong> to the organization.",
"org.teams.owners_permission_suggestion": "You can create new teams for members to get fine-grained access control.",
"org.teams.members": "Team Members",
"org.teams.manage_team_member": "Manage teams and members",
"org.teams.manage_team_member_prompt": "Members are managed through teams. Add users to a team to invite them to this organization.",
"org.teams.update_settings": "Update Settings",
"org.teams.delete_team": "Delete Team",
"org.teams.add_team_member": "Add Team Member",
@ -3715,6 +3722,8 @@
"actions.runs.workflow_run_count_1": "%d workflow run",
"actions.runs.workflow_run_count_n": "%d workflow runs",
"actions.runs.commit": "Commit",
"actions.runs.run_details": "Run Details",
"actions.runs.workflow_file": "Workflow file",
"actions.runs.scheduled": "Scheduled",
"actions.runs.pushed_by": "pushed by",
"actions.runs.invalid_workflow_helper": "Workflow config file is invalid. Please check your config file: %s",

View File

@ -2,7 +2,7 @@
"type": "module",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">= 22.6.0",
"node": ">= 22.18.0",
"pnpm": ">= 10.0.0"
},
"dependencies": {
@ -10,12 +10,28 @@
"@citation-js/plugin-bibtex": "0.7.21",
"@citation-js/plugin-csl": "0.7.22",
"@citation-js/plugin-software-formats": "0.6.2",
"@codemirror/autocomplete": "6.20.1",
"@codemirror/commands": "6.10.3",
"@codemirror/lang-json": "6.0.2",
"@codemirror/lang-markdown": "6.5.0",
"@codemirror/language": "6.12.3",
"@codemirror/language-data": "6.5.2",
"@codemirror/legacy-modes": "6.5.2",
"@codemirror/lint": "6.9.5",
"@codemirror/search": "6.6.0",
"@codemirror/state": "6.6.0",
"@codemirror/view": "6.41.0",
"@github/markdown-toolbar-element": "2.2.3",
"@github/paste-markdown": "1.5.3",
"@github/text-expander-element": "2.9.4",
"@lezer/highlight": "1.2.3",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@mermaid-js/layout-elk": "0.2.1",
"@primer/octicons": "19.23.1",
"@replit/codemirror-indentation-markers": "6.5.3",
"@replit/codemirror-lang-nix": "6.0.1",
"@replit/codemirror-lang-svelte": "6.0.0",
"@replit/codemirror-vscode-keymap": "6.0.2",
"@resvg/resvg-wasm": "2.6.2",
"@silverwind/vue3-calendar-heatmap": "2.1.1",
"@vitejs/plugin-vue": "6.0.5",
@ -25,6 +41,7 @@
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
"clippie": "4.1.10",
"codemirror-lang-elixir": "4.0.1",
"colord": "2.9.3",
"compare-versions": "6.1.1",
"cropperjs": "1.6.2",
@ -36,9 +53,8 @@
"idiomorph": "0.7.4",
"jquery": "4.0.0",
"js-yaml": "4.1.1",
"katex": "0.16.43",
"mermaid": "11.13.0",
"monaco-editor": "0.55.1",
"katex": "0.16.44",
"mermaid": "11.14.0",
"online-3d-viewer": "0.18.0",
"pdfobject": "2.3.1",
"perfect-debounce": "2.1.0",
@ -63,9 +79,9 @@
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "4.7.1",
"@eslint/json": "1.2.0",
"@playwright/test": "1.58.2",
"@playwright/test": "1.59.0",
"@stylistic/eslint-plugin": "5.10.0",
"@stylistic/stylelint-plugin": "5.0.1",
"@stylistic/stylelint-plugin": "5.1.0",
"@types/codemirror": "5.60.17",
"@types/dropzone": "5.7.9",
"@types/jquery": "4.0.0",
@ -77,9 +93,9 @@
"@types/swagger-ui-dist": "3.30.6",
"@types/throttle-debounce": "5.0.2",
"@types/toastify-js": "1.12.4",
"@typescript-eslint/parser": "8.57.2",
"@typescript-eslint/parser": "8.58.0",
"@vitejs/plugin-vue": "6.0.5",
"@vitest/eslint-plugin": "1.6.13",
"@vitest/eslint-plugin": "1.6.14",
"eslint": "10.1.0",
"eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-array-func": "5.1.1",
@ -89,15 +105,15 @@
"eslint-plugin-playwright": "2.10.1",
"eslint-plugin-regexp": "3.1.0",
"eslint-plugin-sonarjs": "4.0.2",
"eslint-plugin-unicorn": "63.0.0",
"eslint-plugin-unicorn": "64.0.0",
"eslint-plugin-vue": "10.8.0",
"eslint-plugin-vue-scoped-css": "3.0.0",
"eslint-plugin-wc": "3.1.0",
"globals": "17.4.0",
"happy-dom": "20.8.8",
"happy-dom": "20.8.9",
"jiti": "2.6.1",
"markdownlint-cli": "0.48.0",
"material-icon-theme": "5.32.0",
"material-icon-theme": "5.33.1",
"nolyfill": "1.0.44",
"postcss-html": "1.8.1",
"spectral-cli-bundle": "1.0.7",
@ -107,9 +123,9 @@
"stylelint-declaration-strict-value": "1.11.1",
"stylelint-value-no-unknown-custom-properties": "6.1.1",
"svgo": "4.0.1",
"typescript": "5.9.3",
"typescript-eslint": "8.57.2",
"updates": "17.12.0",
"typescript": "6.0.2",
"typescript-eslint": "8.58.0",
"updates": "17.13.1",
"vitest": "4.1.2",
"vue-tsc": "3.2.6"
},

View File

@ -1,21 +1,24 @@
import {env} from 'node:process';
import {defineConfig, devices} from '@playwright/test';
const timeoutFactor = Number(env.GITEA_TEST_E2E_TIMEOUT_FACTOR) || 1;
const timeout = 5000 * timeoutFactor;
export default defineConfig({
testDir: './tests/e2e/',
outputDir: './tests/e2e-output/',
testMatch: /.*\.test\.ts/,
forbidOnly: Boolean(env.CI),
reporter: 'list',
timeout: env.CI ? 12000 : 6000,
timeout: 2 * timeout,
expect: {
timeout: env.CI ? 6000 : 3000,
timeout,
},
use: {
baseURL: env.GITEA_TEST_E2E_URL?.replace?.(/\/$/g, ''),
baseURL: env.GITEA_TEST_E2E_URL?.replace?.(/\/$/, ''),
locale: 'en-US',
actionTimeout: env.CI ? 6000 : 3000,
navigationTimeout: env.CI ? 12000 : 6000,
actionTimeout: timeout,
navigationTimeout: 2 * timeout,
},
projects: [
{

1674
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -726,6 +726,9 @@ func EditIssue(ctx *context.APIContext) {
// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue
// ---
// summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored.
// description: |
// Pass `content_version` to enable optimistic locking on body edits.
// If the version doesn't match the current value, the request fails with 409 Conflict.
// consumes:
// - application/json
// produces:
@ -785,6 +788,15 @@ func EditIssue(ctx *context.APIContext) {
return
}
// Fail fast: if content_version is provided and already stale, reject
// before any mutations. The DB-level check in ChangeContent still
// handles concurrent requests.
// TODO: wrap all mutations in a transaction to fully prevent partial writes.
if form.ContentVersion != nil && *form.ContentVersion != issue.ContentVersion {
ctx.APIError(http.StatusConflict, issues_model.ErrIssueAlreadyChanged)
return
}
if len(form.Title) > 0 {
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
if err != nil {
@ -793,10 +805,14 @@ func EditIssue(ctx *context.APIContext) {
}
}
if form.Body != nil {
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
contentVersion := issue.ContentVersion
if form.ContentVersion != nil {
contentVersion = *form.ContentVersion
}
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, contentVersion)
if err != nil {
if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
ctx.APIError(http.StatusBadRequest, err)
ctx.APIError(http.StatusConflict, err)
return
}

View File

@ -657,6 +657,15 @@ func EditPullRequest(ctx *context.APIContext) {
return
}
// Fail fast: if content_version is provided and already stale, reject
// before any mutations. The DB-level check in ChangeContent still
// handles concurrent requests.
// TODO: wrap all mutations in a transaction to fully prevent partial writes.
if form.ContentVersion != nil && *form.ContentVersion != issue.ContentVersion {
ctx.APIError(http.StatusConflict, issues_model.ErrIssueAlreadyChanged)
return
}
if len(form.Title) > 0 {
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
if err != nil {
@ -665,10 +674,14 @@ func EditPullRequest(ctx *context.APIContext) {
}
}
if form.Body != nil {
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
contentVersion := issue.ContentVersion
if form.ContentVersion != nil {
contentVersion = *form.ContentVersion
}
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, contentVersion)
if err != nil {
if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
ctx.APIError(http.StatusBadRequest, err)
ctx.APIError(http.StatusConflict, err)
return
}

View File

@ -215,6 +215,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
w := &webhook.Webhook{
OwnerID: ownerID,
RepoID: repoID,
Name: strings.TrimSpace(form.Name),
URL: form.Config["url"],
ContentType: webhook.ToHookContentType(form.Config["content_type"]),
Secret: form.Config["secret"],
@ -392,6 +393,10 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
w.IsActive = *form.Active
}
if form.Name != nil {
w.Name = strings.TrimSpace(*form.Name)
}
if err := webhook.UpdateWebhook(ctx, w); err != nil {
ctx.APIErrorInternal(err)
return false

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/routing"
@ -40,6 +41,10 @@ func ProtocolMiddlewares() (handlers []any) {
handlers = append(handlers, context.AccessLogger())
}
if !setting.IsProd {
handlers = append(handlers, public.ViteDevMiddleware)
}
return handlers
}

View File

@ -218,18 +218,50 @@ func performAutoLogin(ctx *context.Context) bool {
return false
}
func prepareSignInPageData(ctx *context.Context) {
func performAutoLoginOAuth2(ctx *context.Context, data *preparedSignInData) bool {
// If only 1 OAuth provider is present and other login methods are disabled, redirect to the OAuth provider.
onlySingleOAuth2 := len(data.oauth2Providers) == 1 &&
!setting.Service.EnablePasswordSignInForm &&
!setting.Service.EnableOpenIDSignIn &&
!setting.Service.EnablePasskeyAuth &&
!data.enableSSPI
if !onlySingleOAuth2 {
return false
}
skipToOAuthURL := setting.AppSubURL + "/user/oauth2/" + url.QueryEscape(data.oauth2Providers[0].DisplayName())
if redirectTo := ctx.FormString("redirect_to"); redirectTo != "" {
skipToOAuthURL += "?redirect_to=" + url.QueryEscape(redirectTo)
}
ctx.Redirect(skipToOAuthURL)
return true
}
type preparedSignInData struct {
oauth2Providers []oauth2.Provider
enableSSPI bool
}
func prepareSignInPageData(ctx *context.Context) (ret preparedSignInData) {
var err error
ret.enableSSPI = auth.IsSSPIEnabled(ctx)
ret.oauth2Providers, err = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
if err != nil {
log.Error("Failed to get OAuth2 providers: %v", err)
}
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["OAuth2Providers"], _ = oauth2.GetOAuth2Providers(ctx, optional.Some(true))
ctx.Data["OAuth2Providers"] = ret.oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
ctx.Data["EnableSSPI"] = ret.enableSSPI
prepareCommonAuthPageData(ctx, CommonAuthOptions{
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin,
})
return ret
}
// SignIn render sign in page
@ -241,7 +273,10 @@ func SignIn(ctx *context.Context) {
redirectAfterAuth(ctx)
return
}
prepareSignInPageData(ctx)
data := prepareSignInPageData(ctx)
if performAutoLoginOAuth2(ctx, &data) {
return
}
ctx.HTML(http.StatusOK, tplSignIn)
}
@ -468,6 +503,7 @@ func prepareSignUpPageData(ctx *context.Context) bool {
ctx.Data["Title"] = ctx.Tr("sign_up")
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
ctx.Data["PageIsSignUp"] = true
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
hasUsers, err := user_model.HasUsers(ctx)
if err != nil {

View File

@ -96,6 +96,37 @@ func TestWebAuthOAuth2(t *testing.T) {
assert.Contains(t, ctx.Flash.ErrorMsg, "auth.oauth.signin.error.general")
})
t.Run("RedirectSingleProvider", func(t *testing.T) {
enablePassword := &setting.Service.EnablePasswordSignInForm
enableOpenID := &setting.Service.EnableOpenIDSignIn
enablePasskey := &setting.Service.EnablePasskeyAuth
defer test.MockVariableValue(enablePassword, false)()
defer test.MockVariableValue(enableOpenID, false)()
defer test.MockVariableValue(enablePasskey, false)()
testSignIn := func(t *testing.T, link string, expectedCode int, expectedRedirect string) {
ctx, resp := contexttest.MockContext(t, link)
SignIn(ctx)
assert.Equal(t, expectedCode, resp.Code)
if expectedCode == http.StatusSeeOther {
assert.Equal(t, expectedRedirect, test.RedirectURL(resp))
}
}
testSignIn(t, "/user/login", http.StatusSeeOther, "/user/oauth2/dummy-auth-source")
testSignIn(t, "/user/login?redirect_to=/", http.StatusSeeOther, "/user/oauth2/dummy-auth-source?redirect_to=%2F")
*enablePassword, *enableOpenID, *enablePasskey = true, false, false
testSignIn(t, "/user/login", http.StatusOK, "")
*enablePassword, *enableOpenID, *enablePasskey = false, true, false
testSignIn(t, "/user/login", http.StatusOK, "")
*enablePassword, *enableOpenID, *enablePasskey = false, false, true
testSignIn(t, "/user/login", http.StatusOK, "")
*enablePassword, *enableOpenID, *enablePasskey = false, false, false
addOAuth2Source(t, "dummy-auth-source-2", oauth2.Source{})
testSignIn(t, "/user/login", http.StatusOK, "")
})
t.Run("OIDCLogout", func(t *testing.T) {
var mockServer *httptest.Server
mockServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@ -5,6 +5,7 @@
package org
import (
"errors"
"net/http"
"code.gitea.io/gitea/models/organization"
@ -12,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
org_service "code.gitea.io/gitea/services/org"
@ -76,11 +78,11 @@ func Members(ctx *context.Context) {
// MembersAction response for operation to a member of organization
func MembersAction(ctx *context.Context) {
member, err := user_model.GetUserByID(ctx, ctx.FormInt64("uid"))
if err != nil {
log.Error("GetUserByID: %v", err)
}
if member == nil {
ctx.Redirect(ctx.Org.OrgLink + "/members")
if errors.Is(err, util.ErrNotExist) {
ctx.HTTPError(http.StatusNotFound)
return
} else if err != nil {
ctx.ServerError("GetUserByID", err)
return
}
@ -105,40 +107,25 @@ func MembersAction(ctx *context.Context) {
return
}
err = org_service.RemoveOrgUser(ctx, org, member)
if organization.IsErrLastOrgOwner(err) {
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
ctx.JSONRedirect(ctx.Org.OrgLink + "/members")
return
}
case "leave":
err = org_service.RemoveOrgUser(ctx, org, ctx.Doer)
if err == nil {
ctx.Flash.Success(ctx.Tr("form.organization_leave_success", org.DisplayName()))
ctx.JSON(http.StatusOK, map[string]any{
"redirect": "", // keep the user stay on current page, in case they want to do other operations.
})
} else if organization.IsErrLastOrgOwner(err) {
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
ctx.JSONRedirect(ctx.Org.OrgLink + "/members")
} else {
log.Error("RemoveOrgUser(%d,%d): %v", org.ID, ctx.Doer.ID, err)
ctx.JSONRedirect(setting.AppSubURL + "/")
return
}
}
if err == nil {
ctx.JSONOK()
return
}
if err != nil {
log.Error("Action(%s): %v", ctx.PathParam("action"), err)
ctx.JSON(http.StatusOK, map[string]any{
"ok": false,
"err": err.Error(),
})
if organization.IsErrLastOrgOwner(err) {
ctx.JSONError(ctx.Tr("form.last_org_owner"))
return
}
redirect := ctx.Org.OrgLink + "/members"
if ctx.PathParam("action") == "leave" {
redirect = setting.AppSubURL + "/"
}
ctx.JSONRedirect(redirect)
log.Error("Action(%s): %v", ctx.PathParam("action"), err)
ctx.JSONError(err.Error()) // FIXME: legacy logic, errors are handled together, it's not right, need to distinguish between different errors
}

View File

@ -319,7 +319,12 @@ func EditFile(ctx *context.Context) {
}
}
ctx.Data["CodeEditorConfig"] = getCodeEditorConfig(ctx, ctx.Repo.TreePath)
editorConfig := getCodeEditorConfigByEditorconfig(ctx, ctx.Repo.TreePath)
editorConfig.Autofocus = !isNewFile
if isNewFile {
editorConfig.Filename = ""
}
ctx.Data["CodeEditorConfig"] = editorConfig
ctx.HTML(http.StatusOK, tplEditFile)
}

View File

@ -20,7 +20,7 @@ func NewDiffPatch(ctx *context.Context) {
}
ctx.Data["PageIsPatch"] = true
ctx.Data["CodeEditorConfig"] = CodeEditorConfig{} // not really editing a file, so no need to fill in the config
ctx.Data["CodeEditorConfig"] = CodeEditorConfig{Filename: "diff.patch"}
ctx.HTML(http.StatusOK, tplPatchFile)
}

View File

@ -65,27 +65,33 @@ func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePa
return f(originTreePath, commit)
}
// CodeEditorConfig is also used by frontend, defined in "codeeditor.ts"
// CodeEditorConfig is also used by frontend, defined in "codeeditor" module
type CodeEditorConfig struct {
PreviewableExtensions []string `json:"previewable_extensions"`
LineWrapExtensions []string `json:"line_wrap_extensions"`
LineWrapOn bool `json:"line_wrap_on"`
Filename string `json:"filename"` // the base name, not full path
Autofocus bool `json:"autofocus"`
PreviewableExtensions []string `json:"previewableExtensions,omitempty"`
LineWrapExtensions []string `json:"lineWrapExtensions,omitempty"`
LineWrap bool `json:"lineWrap"`
Previewable bool `json:"previewable,omitempty"`
IndentStyle string `json:"indent_style"`
IndentSize int `json:"indent_size"`
TabWidth int `json:"tab_width"`
TrimTrailingWhitespace *bool `json:"trim_trailing_whitespace,omitempty"`
// the following can be read from .editorconfig if exists, or use default value
IndentStyle string `json:"indentStyle"` // in most cases, keep it empty by default, detected by the source code
IndentSize int `json:"indentSize"`
TabWidth int `json:"tabWidth"`
TrimTrailingWhitespace *bool `json:"trimTrailingWhitespace,omitempty"`
}
func getCodeEditorConfig(ctx *context_service.Context, treePath string) (ret CodeEditorConfig) {
func getCodeEditorConfigByEditorconfig(ctx *context_service.Context, treePath string) CodeEditorConfig {
ret := CodeEditorConfig{Filename: path.Base(treePath)}
ret.PreviewableExtensions = markup.PreviewableExtensions()
ret.LineWrapExtensions = setting.Repository.Editor.LineWrapExtensions
ret.LineWrapOn = util.SliceContainsString(ret.LineWrapExtensions, path.Ext(treePath), true)
ret.LineWrap = util.SliceContainsString(ret.LineWrapExtensions, path.Ext(treePath), true)
ret.Previewable = util.SliceContainsString(ret.PreviewableExtensions, path.Ext(treePath), true)
ec, _, err := ctx.Repo.GetEditorconfig()
if err == nil {
def, err := ec.GetDefinitionForFilename(treePath)
if err == nil {
ret.IndentStyle = def.IndentStyle
ret.IndentStyle = util.IfZero(def.IndentStyle, ret.IndentStyle)
ret.IndentSize, _ = strconv.Atoi(def.IndentSize)
ret.TabWidth = def.TabWidth
ret.TrimTrailingWhitespace = def.TrimTrailingWhitespace

View File

@ -121,11 +121,9 @@ func NewIssue(ctx *context.Context) {
}
pageMetaData.MilestonesData.SelectedMilestoneID = ctx.FormInt64("milestone")
pageMetaData.ProjectsData.SelectedProjectID = ctx.FormInt64("project")
if pageMetaData.ProjectsData.SelectedProjectID > 0 {
if len(ctx.Req.URL.Query().Get("project")) > 0 {
ctx.Data["redirect_after_creation"] = "project"
}
pageMetaData.ProjectsData.SelectedProjectIDs, _ = base.StringsToInt64s(strings.Split(ctx.FormString("project"), ","))
if len(pageMetaData.ProjectsData.SelectedProjectIDs) == 1 {
ctx.Data["redirect_after_creation"] = "project"
}
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
@ -273,7 +271,7 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
ctx.NotFound(nil)
return ret
}
pageMetaData.ProjectsData.SelectedProjectID = form.ProjectID
pageMetaData.ProjectsData.SelectedProjectIDs = util.Iif(form.ProjectID > 0, []int64{form.ProjectID}, nil)
// prepare assignees
candidateAssignees := toSet(pageMetaData.AssigneesData.CandidateAssignees, func(user *user_model.User) int64 { return user.ID })

View File

@ -34,9 +34,14 @@ type issueSidebarAssigneesData struct {
}
type issueSidebarProjectsData struct {
SelectedProjectID int64
OpenProjects []*project_model.Project
ClosedProjects []*project_model.Project
SelectedProjectIDs []int64 // TODO: support multiple projects in the future
// the "selected" fields are only valid when len(SelectedProjectIDs)==1
SelectedProjectColumns []*project_model.Column
SelectedProjectColumn *project_model.Column
OpenProjects []*project_model.Project
ClosedProjects []*project_model.Project
}
type IssuePageMetaData struct {
@ -92,6 +97,11 @@ func retrieveRepoIssueMetaData(ctx *context.Context, repo *repo_model.Repository
return data
}
data.retrieveProjectData(ctx)
if ctx.Written() {
return data
}
// TODO: the issue/pull permissions are quite complex and unclear
// A reader could create an issue/PR with setting some meta (eg: assignees from issue template, reviewers, target branch)
// A reader(creator) could update some meta (eg: target branch), but can't change assignees anymore.
@ -158,9 +168,33 @@ func (d *IssuePageMetaData) retrieveAssigneesData(ctx *context.Context) {
ctx.Data["Assignees"] = d.AssigneesData.CandidateAssignees
}
func (d *IssuePageMetaData) retrieveProjectData(ctx *context.Context) {
if d.Issue == nil || d.Issue.Project == nil {
return
}
d.ProjectsData.SelectedProjectIDs = []int64{d.Issue.Project.ID}
columns, err := d.Issue.Project.GetColumns(ctx)
if err != nil {
ctx.ServerError("GetProjectColumns", err)
return
}
d.ProjectsData.SelectedProjectColumns = columns
columnID, err := d.Issue.ProjectColumnID(ctx)
if err != nil {
ctx.ServerError("ProjectColumnID", err)
return
}
for _, col := range columns {
if col.ID == columnID {
d.ProjectsData.SelectedProjectColumn = col
break
}
}
}
func (d *IssuePageMetaData) retrieveProjectsDataForIssueWriter(ctx *context.Context) {
if d.Issue != nil && d.Issue.Project != nil {
d.ProjectsData.SelectedProjectID = d.Issue.Project.ID
d.ProjectsData.SelectedProjectIDs = []int64{d.Issue.Project.ID}
}
d.ProjectsData.OpenProjects, d.ProjectsData.ClosedProjects = retrieveProjectsInternal(ctx, ctx.Repo.Repository)
}

View File

@ -42,7 +42,7 @@ func GitHooksEdit(ctx *context.Context) {
return
}
ctx.Data["Hook"] = hook
ctx.Data["CodeEditorConfig"] = repo.CodeEditorConfig{} // not really editing a repo file, so no editor config
ctx.Data["CodeEditorConfig"] = repo.CodeEditorConfig{Filename: name + ".sh", IndentStyle: "tab", TabWidth: 4}
ctx.HTML(http.StatusOK, tplGithookEdit)
}

View File

@ -234,6 +234,7 @@ func createWebhook(ctx *context.Context, params webhookParams) {
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: params.URL,
Name: strings.TrimSpace(params.WebhookForm.Name),
HTTPMethod: params.HTTPMethod,
ContentType: params.ContentType,
Secret: params.WebhookForm.Secret,
@ -288,6 +289,7 @@ func editWebhook(ctx *context.Context, params webhookParams) {
}
w.URL = params.URL
w.Name = strings.TrimSpace(params.WebhookForm.Name)
w.ContentType = params.ContentType
w.Secret = params.WebhookForm.Secret
w.HookEvent = ParseHookEvent(params.WebhookForm)

View File

@ -259,10 +259,6 @@ func Routes() *web.Router {
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
routes.BeforeRouting(chi_middleware.GetHead)
if !setting.IsProd {
routes.BeforeRouting(public.ViteDevMiddleware)
}
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
routes.Methods("GET, HEAD, OPTIONS", "/assets/*", routing.MarkLogLevelTrace, optionsCorsHandler(), public.FileHandlerFunc())
routes.Methods("GET, HEAD", "/avatars/*", avatarStorageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))

View File

@ -62,7 +62,7 @@ func (p *AuthSourceProvider) DisplayName() string {
func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
if p.iconURL != "" {
img := fmt.Sprintf(`<img class="tw-object-contain tw-mr-2" width="%d" height="%d" src="%s" alt="%s">`,
img := fmt.Sprintf(`<img class="tw-object-contain" width="%d" height="%d" src="%s" alt="%s">`,
size,
size,
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),

View File

@ -42,10 +42,10 @@ func (b *BaseProvider) IconHTML(size int) template.HTML {
case "github":
svgName = "octicon-mark-github"
}
svgHTML := svg.RenderHTML(svgName, size, "tw-mr-2")
svgHTML := svg.RenderHTML(svgName, size)
if svgHTML == "" {
log.Error("No SVG icon for oauth2 provider %q", b.name)
svgHTML = svg.RenderHTML("gitea-openid", size, "tw-mr-2")
svgHTML = svg.RenderHTML("gitea-openid", size)
}
return svgHTML
}

View File

@ -33,7 +33,7 @@ func (o *OpenIDProvider) DisplayName() string {
// IconHTML returns icon HTML for this provider
func (o *OpenIDProvider) IconHTML(size int) template.HTML {
return svg.RenderHTML("gitea-openid", size, "tw-mr-2")
return svg.RenderHTML("gitea-openid", size)
}
// CreateGothProvider creates a GothProvider from this Provider

View File

@ -62,7 +62,8 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
Updated: issue.UpdatedUnix.AsTime(),
PinOrder: util.Iif(issue.PinOrder == -1, 0, issue.PinOrder), // -1 means loaded with no pin order
TimeEstimate: issue.TimeEstimate,
TimeEstimate: issue.TimeEstimate,
ContentVersion: issue.ContentVersion,
}
if issue.Repo != nil {

View File

@ -97,6 +97,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
ContentVersion: apiIssue.ContentVersion,
// output "[]" rather than null to align to github outputs
RequestedReviewers: []*api.User{},
@ -372,6 +373,7 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
ContentVersion: apiIssue.ContentVersion,
AllowMaintainerEdit: pr.AllowMaintainerEdit,

View File

@ -10,13 +10,22 @@ import (
"time"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/translation"
"github.com/go-co-op/gocron"
"github.com/go-co-op/gocron/v2"
)
var scheduler = gocron.NewScheduler(time.Local)
var scheduler gocron.Scheduler
func init() {
var err error
scheduler, err = gocron.NewScheduler(gocron.WithLocation(time.Local))
if err != nil {
log.Fatal("Unable to create cron scheduler: %v", err)
}
}
// Init begins cron tasks
// Each cron task is run within the shutdown context as a running server
@ -35,11 +44,13 @@ func Init(original context.Context) {
}
}
scheduler.StartAsync()
scheduler.Start()
started = true
lock.Unlock()
graceful.GetManager().RunAtShutdown(context.Background(), func() {
scheduler.Stop()
if err := scheduler.Shutdown(); err != nil {
log.Error("Unable to shutdown cron scheduler: %v", err)
}
lock.Lock()
started = false
lock.Unlock()
@ -74,14 +85,14 @@ type TaskTable []*TaskTableRow
// ListTasks returns all running cron tasks.
func ListTasks() TaskTable {
jobs := scheduler.Jobs()
jobMap := map[string]*gocron.Job{}
jobMap := map[string]gocron.Job{}
for _, job := range jobs {
// the first tag is the task name
tags := job.Tags()
if len(tags) == 0 { // should never happen
continue
}
jobMap[job.Tags()[0]] = job
jobMap[tags[0]] = job
}
lock.Lock()
@ -99,8 +110,8 @@ func ListTasks() TaskTable {
if len(tags) > 1 {
spec = tags[1] // the second tag is the task spec
}
next = e.NextRun()
prev = e.PreviousRun()
next, _ = e.NextRun()
prev, _ = e.LastRun()
}
task.lock.Lock()

View File

@ -20,6 +20,8 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
"github.com/go-co-op/gocron/v2"
)
var (
@ -224,12 +226,13 @@ func RegisterTaskFatal(name string, config Config, fun func(context.Context, *us
func addTaskToScheduler(task *Task) error {
tags := []string{task.Name, task.config.GetSchedule()} // name and schedule can't be get from job, so we add them as tag
if scheduleHasSeconds(task.config.GetSchedule()) {
scheduler = scheduler.CronWithSeconds(task.config.GetSchedule())
} else {
scheduler = scheduler.Cron(task.config.GetSchedule())
}
if _, err := scheduler.Tag(tags...).Do(task.Run); err != nil {
withSeconds := scheduleHasSeconds(task.config.GetSchedule())
_, err := scheduler.NewJob(
gocron.CronJob(task.config.GetSchedule(), withSeconds),
gocron.NewTask(task.Run),
gocron.WithTags(tags...),
)
if err != nil {
log.Error("Unable to register cron task with name: %s Error: %v", task.Name, err)
return err
}

View File

@ -13,7 +13,11 @@ import (
func TestAddTaskToScheduler(t *testing.T) {
assert.Empty(t, scheduler.Jobs())
defer scheduler.Clear()
defer func() {
for _, j := range scheduler.Jobs() {
_ = scheduler.RemoveJob(j.ID())
}
}()
// no seconds
err := addTaskToScheduler(&Task{

View File

@ -206,6 +206,7 @@ type ProtectBranchPriorityForm struct {
// WebhookForm form for changing web hook
type WebhookForm struct {
Name string `binding:"MaxSize(255)"`
Events string
Create bool
Delete bool

View File

@ -21,7 +21,7 @@ import (
"github.com/dimiro1/reply"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
"github.com/jhillyerd/enmime"
"github.com/jhillyerd/enmime/v2"
)
var (

View File

@ -7,7 +7,7 @@ import (
"strings"
"testing"
"github.com/jhillyerd/enmime"
"github.com/jhillyerd/enmime/v2"
"github.com/stretchr/testify/assert"
)

View File

@ -41,19 +41,17 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
}
type ntlmAuth struct {
username, password, domain string
domainNeeded bool
username, password string
}
// NtlmAuth SMTP AUTH NTLM Auth Handler
func NtlmAuth(username, password string) smtp.Auth {
user, domain, domainNeeded := ntlmssp.GetDomain(username)
return &ntlmAuth{user, password, domain, domainNeeded}
return &ntlmAuth{username, password}
}
// Start starts SMTP NTLM Auth
func (a *ntlmAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
negotiateMessage, err := ntlmssp.NewNegotiateMessage(a.domain, "")
negotiateMessage, err := ntlmssp.NewNegotiateMessage("", "")
return "NTLM", negotiateMessage, err
}
@ -63,7 +61,7 @@ func (a *ntlmAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if len(fromServer) == 0 {
return nil, errors.New("ntlm ChallengeMessage is empty")
}
authenticateMessage, err := ntlmssp.ProcessChallenge(fromServer, a.username, a.password, a.domainNeeded)
authenticateMessage, err := ntlmssp.NewAuthenticateMessage(fromServer, a.username, a.password, nil)
return authenticateMessage, err
}
return nil, nil

View File

@ -7,7 +7,7 @@ package migrations
import (
"errors"
"github.com/google/go-github/v74/github"
"github.com/google/go-github/v84/github"
)
// ErrRepoNotCreated returns the error that repository not created

View File

@ -20,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/structs"
"github.com/google/go-github/v74/github"
"github.com/google/go-github/v84/github"
"golang.org/x/oauth2"
)
@ -861,28 +861,22 @@ func (g *GithubDownloaderV3) GetReviews(ctx context.Context, reviewable base.Rev
opt.Page = resp.NextPage
}
// Get requested reviews
for {
g.waitAndPickClient(ctx)
reviewers, resp, err := g.getClient().PullRequests.ListReviewers(ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), opt)
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
g.setRate(&resp.Rate)
for _, user := range reviewers.Users {
r := &base.Review{
ReviewerID: user.GetID(),
ReviewerName: user.GetLogin(),
State: base.ReviewStateRequestReview,
IssueIndex: reviewable.GetLocalIndex(),
}
allReviews = append(allReviews, r)
}
// TODO: Handle Team requests
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
g.waitAndPickClient(ctx)
reviewers, resp, err := g.getClient().PullRequests.ListReviewers(ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()))
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
g.setRate(&resp.Rate)
for _, user := range reviewers.Users {
r := &base.Review{
ReviewerID: user.GetID(),
ReviewerName: user.GetLogin(),
State: base.ReviewStateRequestReview,
IssueIndex: reviewable.GetLocalIndex(),
}
allReviews = append(allReviews, r)
}
// TODO: Handle Team requests
return allReviews, nil
}

View File

@ -63,16 +63,16 @@ type gitlabIIDResolver struct {
frozen bool
}
func (r *gitlabIIDResolver) recordIssueIID(issueIID int) {
func (r *gitlabIIDResolver) recordIssueIID(issueIID int64) {
if r.frozen {
panic("cannot record issue IID after pull request IID generation has started")
}
r.maxIssueIID = max(r.maxIssueIID, int64(issueIID))
r.maxIssueIID = max(r.maxIssueIID, issueIID)
}
func (r *gitlabIIDResolver) generatePullRequestNumber(mrIID int) int64 {
func (r *gitlabIIDResolver) generatePullRequestNumber(mrIID int64) int64 {
r.frozen = true
return r.maxIssueIID + int64(mrIID)
return r.maxIssueIID + mrIID
}
// GitlabDownloader implements a Downloader interface to get repository information
@ -83,7 +83,7 @@ type GitlabDownloader struct {
base.NullDownloader
client *gitlab.Client
baseURL string
repoID int
repoID int64
repoName string
iidResolver gitlabIIDResolver
maxPerPage int
@ -212,8 +212,8 @@ func (g *GitlabDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone
ms, _, err := g.client.Milestones.ListMilestones(g.repoID, &gitlab.ListMilestonesOptions{
State: &state,
ListOptions: gitlab.ListOptions{
Page: i,
PerPage: perPage,
Page: int64(i),
PerPage: int64(perPage),
},
}, nil, gitlab.WithContext(ctx))
if err != nil {
@ -281,8 +281,8 @@ func (g *GitlabDownloader) GetLabels(ctx context.Context) ([]*base.Label, error)
labels := make([]*base.Label, 0, perPage)
for i := 1; ; i++ {
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
Page: i,
PerPage: perPage,
Page: int64(i),
PerPage: int64(perPage),
}}, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
@ -310,7 +310,7 @@ func (g *GitlabDownloader) convertGitlabRelease(ctx context.Context, rel *gitlab
Name: rel.Name,
Body: rel.Description,
Created: *rel.CreatedAt,
PublisherID: int64(rel.Author.ID),
PublisherID: rel.Author.ID,
PublisherName: rel.Author.Username,
}
@ -319,7 +319,7 @@ func (g *GitlabDownloader) convertGitlabRelease(ctx context.Context, rel *gitlab
for _, asset := range rel.Assets.Links {
assetID := asset.ID // Don't optimize this, for closure we need a local variable
r.Assets = append(r.Assets, &base.ReleaseAsset{
ID: int64(asset.ID),
ID: asset.ID,
Name: asset.Name,
Size: &zero,
DownloadCount: &zero,
@ -359,8 +359,8 @@ func (g *GitlabDownloader) GetReleases(ctx context.Context) ([]*base.Release, er
for i := 1; ; i++ {
ls, _, err := g.client.Releases.ListReleases(g.repoID, &gitlab.ListReleasesOptions{
ListOptions: gitlab.ListOptions{
Page: i,
PerPage: perPage,
Page: int64(i),
PerPage: int64(perPage),
},
}, nil, gitlab.WithContext(ctx))
if err != nil {
@ -396,8 +396,8 @@ func (g *GitlabDownloader) GetIssues(ctx context.Context, page, perPage int) ([]
State: &state,
Sort: &sort,
ListOptions: gitlab.ListOptions{
PerPage: perPage,
Page: page,
PerPage: int64(perPage),
Page: int64(page),
},
}
@ -423,7 +423,7 @@ func (g *GitlabDownloader) GetIssues(ctx context.Context, page, perPage int) ([]
var reactions []*gitlab.AwardEmoji
awardPage := 1
for {
awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(ctx))
awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{ListOptions: gitlab.ListOptions{Page: int64(awardPage), PerPage: int64(perPage)}}, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing issue awards: %w", err)
}
@ -439,8 +439,8 @@ func (g *GitlabDownloader) GetIssues(ctx context.Context, page, perPage int) ([]
allIssues = append(allIssues, &base.Issue{
Title: issue.Title,
Number: int64(issue.IID),
PosterID: int64(issue.Author.ID),
Number: issue.IID,
PosterID: issue.Author.ID,
PosterName: issue.Author.Username,
Content: issue.Description,
Milestone: milestone,
@ -451,7 +451,7 @@ func (g *GitlabDownloader) GetIssues(ctx context.Context, page, perPage int) ([]
Closed: issue.ClosedAt,
IsLocked: issue.DiscussionLocked,
Updated: *issue.UpdatedAt,
ForeignIndex: int64(issue.IID),
ForeignIndex: issue.IID,
Context: gitlabIssueContext{IsMergeRequest: false},
})
@ -472,21 +472,25 @@ func (g *GitlabDownloader) GetComments(ctx context.Context, commentable base.Com
allComments := make([]*base.Comment, 0, g.maxPerPage)
page := 1
var page int64 = 1
for {
var comments []*gitlab.Discussion
var resp *gitlab.Response
var err error
if !context.IsMergeRequest {
comments, resp, err = g.client.Discussions.ListIssueDiscussions(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListIssueDiscussionsOptions{
Page: page,
PerPage: g.maxPerPage,
comments, resp, err = g.client.Discussions.ListIssueDiscussions(g.repoID, commentable.GetForeignIndex(), &gitlab.ListIssueDiscussionsOptions{
ListOptions: gitlab.ListOptions{
Page: page,
PerPage: int64(g.maxPerPage),
},
}, nil, gitlab.WithContext(ctx))
} else {
comments, resp, err = g.client.Discussions.ListMergeRequestDiscussions(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListMergeRequestDiscussionsOptions{
Page: page,
PerPage: g.maxPerPage,
comments, resp, err = g.client.Discussions.ListMergeRequestDiscussions(g.repoID, commentable.GetForeignIndex(), &gitlab.ListMergeRequestDiscussionsOptions{
ListOptions: gitlab.ListOptions{
Page: page,
PerPage: int64(g.maxPerPage),
},
}, nil, gitlab.WithContext(ctx))
}
@ -510,17 +514,17 @@ func (g *GitlabDownloader) GetComments(ctx context.Context, commentable base.Com
var resp *gitlab.Response
var err error
if context.IsMergeRequest {
stateEvents, resp, err = g.client.ResourceStateEvents.ListMergeStateEvents(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListStateEventsOptions{
stateEvents, resp, err = g.client.ResourceStateEvents.ListMergeStateEvents(g.repoID, commentable.GetForeignIndex(), &gitlab.ListStateEventsOptions{
ListOptions: gitlab.ListOptions{
Page: page,
PerPage: g.maxPerPage,
PerPage: int64(g.maxPerPage),
},
}, nil, gitlab.WithContext(ctx))
} else {
stateEvents, resp, err = g.client.ResourceStateEvents.ListIssueStateEvents(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListStateEventsOptions{
stateEvents, resp, err = g.client.ResourceStateEvents.ListIssueStateEvents(g.repoID, commentable.GetForeignIndex(), &gitlab.ListStateEventsOptions{
ListOptions: gitlab.ListOptions{
Page: page,
PerPage: g.maxPerPage,
PerPage: int64(g.maxPerPage),
},
}, nil, gitlab.WithContext(ctx))
}
@ -531,11 +535,11 @@ func (g *GitlabDownloader) GetComments(ctx context.Context, commentable base.Com
for _, stateEvent := range stateEvents {
posterUserID, posterUsername := user.GhostUserID, user.GhostUserName
if stateEvent.User != nil {
posterUserID, posterUsername = int64(stateEvent.User.ID), stateEvent.User.Username
posterUserID, posterUsername = stateEvent.User.ID, stateEvent.User.Username
}
comment := &base.Comment{
IssueIndex: commentable.GetLocalIndex(),
Index: int64(stateEvent.ID),
Index: stateEvent.ID,
PosterID: posterUserID,
PosterName: posterUsername,
Content: "",
@ -569,8 +573,8 @@ var targetBranchChangeRegexp = regexp.MustCompile("^changed target branch from `
func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.Note) *base.Comment {
comment := &base.Comment{
IssueIndex: localIndex,
Index: int64(note.ID),
PosterID: int64(note.Author.ID),
Index: note.ID,
PosterID: note.Author.ID,
PosterName: note.Author.Username,
PosterEmail: note.Author.Email,
Content: note.Body,
@ -603,8 +607,8 @@ func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage in
view := "simple"
opt := &gitlab.ListProjectMergeRequestsOptions{
ListOptions: gitlab.ListOptions{
PerPage: perPage,
Page: page,
PerPage: int64(perPage),
Page: int64(page),
},
View: &view,
}
@ -664,7 +668,7 @@ func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage in
var reactions []*gitlab.AwardEmoji
awardPage := 1
for {
awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(ctx))
awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{ListOptions: gitlab.ListOptions{Page: int64(awardPage), PerPage: int64(perPage)}}, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing merge requests awards: %w", err)
}
@ -685,7 +689,7 @@ func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage in
Title: pr.Title,
Number: newPRNumber,
PosterName: pr.Author.Username,
PosterID: int64(pr.Author.ID),
PosterID: pr.Author.ID,
Content: pr.Description,
Milestone: milestone,
State: pr.State,
@ -711,7 +715,7 @@ func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage in
OwnerName: pr.Author.Username,
},
PatchURL: pr.WebURL + ".patch",
ForeignIndex: int64(pr.IID),
ForeignIndex: pr.IID,
Context: gitlabIssueContext{IsMergeRequest: true},
IsDraft: pr.Draft,
})
@ -725,7 +729,7 @@ func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage in
// GetReviews returns pull requests review
func (g *GitlabDownloader) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
approvals, resp, err := g.client.MergeRequestApprovals.GetConfiguration(g.repoID, int(reviewable.GetForeignIndex()), gitlab.WithContext(ctx))
approvals, resp, err := g.client.MergeRequestApprovals.GetConfiguration(g.repoID, reviewable.GetForeignIndex(), gitlab.WithContext(ctx))
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
log.Error(fmt.Sprintf("GitlabDownloader: while migrating a error occurred: '%s'", err.Error()))
@ -747,7 +751,7 @@ func (g *GitlabDownloader) GetReviews(ctx context.Context, reviewable base.Revie
for _, user := range approvals.ApprovedBy {
reviews = append(reviews, &base.Review{
IssueIndex: reviewable.GetLocalIndex(),
ReviewerID: int64(user.User.ID),
ReviewerID: user.User.ID,
ReviewerName: user.User.Username,
CreatedAt: createdAt,
// All we get are approvals
@ -765,7 +769,7 @@ func (g *GitlabDownloader) awardsToReactions(awards []*gitlab.AwardEmoji) []*bas
uid := fmt.Sprintf("%s%d", award.Name, award.User.ID)
if uniqCheck.Add(uid) {
result = append(result, &base.Reaction{
UserID: int64(award.User.ID),
UserID: award.User.ID,
UserName: award.User.Username,
Content: award.Name,
})

View File

@ -358,7 +358,7 @@ func gitlabClientMockTeardown(server *httptest.Server) {
}
type reviewTestCase struct {
repoID, prID, reviewerID int
repoID, prID, reviewerID int64
reviewerName string
createdAt, updatedAt *time.Time
expectedCreatedAt time.Time
@ -383,8 +383,8 @@ func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Requ
fmt.Fprint(w, `
{
"id": 5,
"iid": `+strconv.Itoa(t.prID)+`,
"project_id": `+strconv.Itoa(t.repoID)+`,
"iid": `+strconv.FormatInt(t.prID, 10)+`,
"project_id": `+strconv.FormatInt(t.repoID, 10)+`,
"title": "Approvals API",
"description": "Test",
"state": "opened",
@ -398,7 +398,7 @@ func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Requ
"user": {
"name": "Administrator",
"username": "`+t.reviewerName+`",
"id": `+strconv.Itoa(t.reviewerID)+`,
"id": `+strconv.FormatInt(t.reviewerID, 10)+`,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url": "http://localhost:3000/root"
@ -408,8 +408,8 @@ func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Requ
}`)
}
review := base.Review{
IssueIndex: int64(t.prID),
ReviewerID: int64(t.reviewerID),
IssueIndex: t.prID,
ReviewerID: t.reviewerID,
ReviewerName: t.reviewerName,
CreatedAt: t.expectedCreatedAt,
State: "APPROVED",
@ -422,7 +422,7 @@ func TestGitlabGetReviews(t *testing.T) {
mux, server, client := gitlabClientMockSetup(t)
defer gitlabClientMockTeardown(server)
repoID := 1324
var repoID int64 = 1324
ctx := t.Context()
downloader := &GitlabDownloader{
client: client,
@ -463,8 +463,7 @@ func TestGitlabGetReviews(t *testing.T) {
mock, review := convertTestCase(testCase)
mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)
id := int64(testCase.prID)
rvs, err := downloader.GetReviews(ctx, &base.Issue{Number: id, ForeignIndex: id})
rvs, err := downloader.GetReviews(ctx, &base.Issue{Number: testCase.prID, ForeignIndex: testCase.prID})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{&review}, rvs)
}
@ -520,18 +519,10 @@ func TestNoteToComment(t *testing.T) {
downloader := &GitlabDownloader{}
now := time.Now()
makeTestNote := func(id int, body string, system bool) gitlab.Note {
makeTestNote := func(id int64, body string, system bool) gitlab.Note {
return gitlab.Note{
ID: id,
Author: struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
State string `json:"state"`
AvatarURL string `json:"avatar_url"`
WebURL string `json:"web_url"`
}{
Author: gitlab.NoteAuthor{
ID: 72,
Email: "test@example.com",
Username: "test",

View File

@ -5,6 +5,7 @@ package org
import (
"context"
"errors"
"fmt"
"strings"
@ -306,19 +307,19 @@ func removeTeamMember(ctx context.Context, team *organization.Team, user *user_m
return err
}
// Delete access to team repositories.
// Delete access to team repositories. If any user or repo is missing, we can continue.
for _, repo := range repos {
if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil {
if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
// Remove watches from now inaccessible
if err := repo_service.ReconsiderWatches(ctx, repo, user); err != nil {
if err := repo_service.ReconsiderWatches(ctx, repo, user); err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
// Remove issue assignments from now inaccessible
if err := repo_service.ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil {
if err := repo_service.ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
}

View File

@ -87,7 +87,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
}
// LoadIssuesFromProject load issues assigned to each project column inside the given project
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (map[int64]issues_model.IssueList, error) {
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (results map[int64]issues_model.IssueList, _ error) {
issueList, err := issues_model.Issues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
o.ProjectID = project.ID
o.SortType = "project-column-sorting"
@ -95,7 +95,10 @@ func LoadIssuesFromProject(ctx context.Context, project *project_model.Project,
if err != nil {
return nil, err
}
if len(issueList) == 0 {
// if no issue, return directly, then no need to create a default column for an empty project
return results, nil
}
if err := issueList.LoadComments(ctx); err != nil {
return nil, err
}
@ -110,7 +113,7 @@ func LoadIssuesFromProject(ctx context.Context, project *project_model.Project,
return nil, err
}
results := make(map[int64]issues_model.IssueList)
results = make(map[int64]issues_model.IssueList)
for _, issue := range issueList {
projectColumnID, ok := issueColumnMap[issue.ID]
if !ok {

View File

@ -411,6 +411,7 @@ func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
return &api.Hook{
ID: w.ID,
Name: w.Name,
Type: w.Type,
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
Active: w.IsActive,

View File

@ -4,10 +4,14 @@
package webtheme
import (
"io/fs"
"os"
"path"
"regexp"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
@ -16,15 +20,15 @@ import (
"code.gitea.io/gitea/modules/util"
)
type themeCollection struct {
type themeCollectionStruct struct {
lastCheckTime time.Time
usingViteDevMode bool
themeList []*ThemeMetaInfo
themeMap map[string]*ThemeMetaInfo
}
var (
themeMu sync.RWMutex
availableThemes *themeCollection
)
var themeCollection atomic.Pointer[themeCollectionStruct]
const (
fileNamePrefix = "theme-"
@ -140,23 +144,42 @@ func parseThemeMetaInfo(fileName, cssContent string) *ThemeMetaInfo {
return themeInfo
}
func loadThemesFromAssets() (themeList []*ThemeMetaInfo, themeMap map[string]*ThemeMetaInfo) {
cssFiles, err := public.AssetFS().ListFiles("assets/css")
func collectThemeFiles(dirFS fs.ReadDirFS, fsPath string) (themes []*ThemeMetaInfo, _ error) {
files, err := dirFS.ReadDir(fsPath)
if err != nil {
log.Error("Failed to list themes: %v", err)
return nil, nil
return nil, err
}
for _, file := range files {
fileName := file.Name()
if !strings.HasPrefix(fileName, fileNamePrefix) || !strings.HasSuffix(fileName, fileNameSuffix) {
continue
}
content, err := fs.ReadFile(dirFS, path.Join(fsPath, file.Name()))
if err != nil {
log.Error("Failed to read theme file %q: %v", fileName, err)
continue
}
themes = append(themes, parseThemeMetaInfo(fileName, util.UnsafeBytesToString(content)))
}
return themes, nil
}
func loadThemesFromAssets(isViteDevMode bool) (themeList []*ThemeMetaInfo, themeMap map[string]*ThemeMetaInfo) {
var themeDir fs.ReadDirFS
var themePath string
if isViteDevMode {
// In vite dev mode, Vite serves themes directly from source files.
themeDir, themePath = os.DirFS(setting.StaticRootPath).(fs.ReadDirFS), "web_src/css/themes"
} else {
// Without vite dev server, use built assets from AssetFS.
themeDir, themePath = public.AssetFS(), "assets/css"
}
var foundThemes []*ThemeMetaInfo
for _, fileName := range cssFiles {
if strings.HasPrefix(fileName, fileNamePrefix) && strings.HasSuffix(fileName, fileNameSuffix) {
content, err := public.AssetFS().ReadFile("/assets/css/" + fileName)
if err != nil {
log.Error("Failed to read theme file %q: %v", fileName, err)
continue
}
foundThemes = append(foundThemes, parseThemeMetaInfo(fileName, util.UnsafeBytesToString(content)))
}
foundThemes, err := collectThemeFiles(themeDir, themePath)
if err != nil {
log.Error("Failed to load theme files: %v", err)
return themeList, themeMap
}
themeList = foundThemes
@ -187,20 +210,21 @@ func loadThemesFromAssets() (themeList []*ThemeMetaInfo, themeMap map[string]*Th
return themeList, themeMap
}
func getAvailableThemes() (themeList []*ThemeMetaInfo, themeMap map[string]*ThemeMetaInfo) {
themeMu.RLock()
if availableThemes != nil {
themeList, themeMap = availableThemes.themeList, availableThemes.themeMap
}
themeMu.RUnlock()
if len(themeList) != 0 {
return themeList, themeMap
func getAvailableThemes() *themeCollectionStruct {
themes := themeCollection.Load()
now := time.Now()
if themes != nil && now.Sub(themes.lastCheckTime) < time.Second {
return themes
}
themeMu.Lock()
defer themeMu.Unlock()
// no need to double-check "availableThemes.themeList" since the loading isn't really slow, to keep code simple
themeList, themeMap = loadThemesFromAssets()
isViteDevMode := public.IsViteDevMode()
useLoadedThemes := themes != nil && (setting.IsProd || themes.usingViteDevMode == isViteDevMode)
if useLoadedThemes && len(themes.themeList) > 0 {
return themes
}
themeList, themeMap := loadThemesFromAssets(isViteDevMode)
hasAvailableThemes := len(themeList) > 0
if !hasAvailableThemes {
defaultTheme := defaultThemeMetaInfoByInternalName(setting.UI.DefaultTheme)
@ -215,27 +239,19 @@ func getAvailableThemes() (themeList []*ThemeMetaInfo, themeMap map[string]*Them
if themeMap[setting.UI.DefaultTheme] == nil {
setting.LogStartupProblem(1, log.ERROR, "Default theme %q is not available, please correct the '[ui].DEFAULT_THEME' setting in the config file", setting.UI.DefaultTheme)
}
availableThemes = &themeCollection{themeList, themeMap}
return themeList, themeMap
}
// In dev mode, only store the loaded themes if the list is not empty, in case the frontend is still being built.
// TBH, there still could be a data-race that the themes are only partially built then the list is incomplete for first time loading.
// Such edge case can be handled by checking whether the loaded themes are the same in a period or there is a flag file, but it is an over-kill, so, no.
if hasAvailableThemes {
availableThemes = &themeCollection{themeList, themeMap}
}
return themeList, themeMap
}
func GetAvailableThemes() []*ThemeMetaInfo {
themes, _ := getAvailableThemes()
themes = &themeCollectionStruct{now, isViteDevMode, themeList, themeMap}
themeCollection.Store(themes)
return themes
}
func GetAvailableThemes() []*ThemeMetaInfo {
return getAvailableThemes().themeList
}
func GetThemeMetaInfo(internalName string) *ThemeMetaInfo {
_, themeMap := getAvailableThemes()
return themeMap[internalName]
return getAvailableThemes().themeMap[internalName]
}
// GuaranteeGetThemeMetaInfo guarantees to return a non-nil ThemeMetaInfo,

View File

@ -44,7 +44,7 @@ parts:
source: .
stage-packages: [ git, sqlite3, openssh-client ]
build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential]
build-snaps: [ go/1.25/stable, node/22/stable ]
build-snaps: [ go/1.26/stable, node/24/stable ]
build-environment:
- LDFLAGS: ""
override-pull: |

View File

@ -4,17 +4,22 @@
<a target="_blank" href="https://about.gitea.com">{{ctx.Locale.Tr "powered_by" "Gitea"}}</a>
{{end}}
{{if (or .ShowFooterVersion .PageIsAdmin)}}
<span>
{{ctx.Locale.Tr "version"}}:
{{if .IsAdmin}}
<a href="{{AppSubUrl}}/-/admin/config">{{AppVer}}</a>
{{else}}
{{AppVer}}
{{end}}
</span>
{{end}}
{{if and .TemplateLoadTimes ShowFooterTemplateLoadTime}}
{{ctx.Locale.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong>
{{ctx.Locale.Tr "template"}}{{if .TemplateName}} {{.TemplateName}}{{end}}: <strong>{{call .TemplateLoadTimes}}</strong>
<span>
{{ctx.Locale.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong>
{{ctx.Locale.Tr "template"}}{{if .TemplateName}} {{.TemplateName}}{{end}}: <strong>{{call .TemplateLoadTimes}}</strong>
</span>
{{end}}
{{if $.ViteModeIsDev}}<span class="ui basic label primary">ViteDevMode</span>{{end}}
</div>
<div class="right-links" role="group" aria-label="{{ctx.Locale.Tr "aria.footer.links"}}">
<div class="ui dropdown custom" id="footer-theme-selector">

View File

@ -47,7 +47,7 @@
<span class="text">
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
<span class="only-mobile">{{.SignedUser.Name}}</span>
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
</span>
<div class="menu user-menu">
<div class="header">
@ -64,9 +64,9 @@
{{else if .IsSigned}}
{{template "base/head_navbar_icons" dict "ItemExtraClass" "not-mobile" "PageGlobalData" .PageGlobalData}}
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
<span class="text">
<span class="flex-text-block">
{{svg "octicon-plus"}}
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
<span class="only-mobile">{{ctx.Locale.Tr "create_new"}}</span>
</span>
<div class="menu">
@ -93,7 +93,7 @@
{{if .IsAdmin}}{{svg "octicon-shield-check" 16 "navbar-admin-badge"}}{{end}}
</span>
<span class="only-mobile">{{.SignedUser.Name}}</span>
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
<span class="not-mobile flex-text-block">{{svg "octicon-triangle-down"}}</span>
</span>
<div class="menu user-menu">
<div class="header">

View File

@ -4,13 +4,13 @@
{{- $activeStopwatch := call $data.GetActiveStopwatch -}}
{{- $notificationUnreadCount := call $data.GetNotificationUnreadCount -}}
<a class="item active-stopwatch{{if not $activeStopwatch}} tw-hidden{{end}} {{$itemExtraClass}}" {{if $activeStopwatch}}href="{{$activeStopwatch.IssueLink}}" data-seconds="{{$activeStopwatch.Seconds}}"{{end}} title="{{ctx.Locale.Tr "active_stopwatch"}}">
<div class="tw-relative">
<div class="tw-relative flex-text-block">
{{svg "octicon-stopwatch"}}
<span class="header-stopwatch-dot"></span>
</div>
</a>
<a class="item {{$itemExtraClass}}" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}">
<div class="tw-relative">
<div class="tw-relative flex-text-block">
{{svg "octicon-bell"}}
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
</div>

View File

@ -4,6 +4,13 @@
<div class="ui container">
{{template "base/alert" .}}
{{if .IsOrganizationOwner}}
<div class="flex-text-block">
<div class="tw-flex-1">{{ctx.Locale.Tr "org.teams.manage_team_member_prompt"}}</div>
<a class="ui primary button" href="./teams">{{ctx.Locale.Tr "org.teams.manage_team_member"}}</a>
</div>
<div class="divider"></div>
{{end}}
<div class="flex-list">
{{range .Members}}
{{$isPublic := index $.MembersIsPublicMember .ID}}
@ -15,27 +22,27 @@
<div class="flex-item-title">
{{template "shared/user/name" .}}
{{if not $isPublic}}
<span class="ui basic tiny label">{{ctx.Locale.Tr "org.members.private"}}</span>
<span class="ui basic small label">{{ctx.Locale.Tr "org.members.private"}}</span>
{{end}}
</div>
{{if not $.PublicOnly}}
<div class="flex-item-body">
<div class="tw-flex tw-flex-col tw-gap-1">
{{if not $.PublicOnly}}
<div>
{{ctx.Locale.Tr "org.members.member_role"}}
<strong class="flex-text-inline">{{if index $.MembersIsUserOrgOwner .ID}}{{svg "octicon-shield-lock"}} {{ctx.Locale.Tr "org.members.owner"}}{{else}}{{ctx.Locale.Tr "org.members.member"}}{{end}}</strong>
</div>
{{end}}
{{if $.IsOrganizationOwner}}
<div class="flex-item-body">
{{ctx.Locale.Tr "admin.users.2fa"}}
<strong>
{{if index $.MembersTwoFaStatus .ID}}
<span class="tw-text-green">{{svg "octicon-check"}}</span>
{{else}}
{{svg "octicon-x"}}
{{end}}
</strong>
<div>
{{ctx.Locale.Tr "admin.users.2fa"}}:
{{if index $.MembersTwoFaStatus .ID}}
<span class="tw-text-green tw-flex">{{svg "octicon-check"}}</span>
{{else}}
{{svg "octicon-x"}}
{{end}}
</div>
{{end}}
{{end}}
</div>
</div>
<div class="flex-item-trailing">
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}}
@ -46,45 +53,23 @@
{{end}}
{{end}}
{{if eq $.SignedUser.ID .ID}}
<form>
<button class="ui red tiny button delete-button" data-modal-id="leave-organization"
data-url="{{$.OrgLink}}/members/action/leave" data-datauid="{{.ID}}"
data-name="{{.DisplayName}}"
data-data-organization-name="{{$.Org.DisplayName}}">{{ctx.Locale.Tr "org.members.leave"}}</button>
</form>
<button class="ui red tiny button link-action"
data-url="{{$.OrgLink}}/members/action/leave?uid={{.ID}}"
data-modal-confirm-header="{{ctx.Locale.Tr "org.members.leave"}}"
data-modal-confirm-content="{{ctx.Locale.Tr "org.members.leave.detail" $.Org.DisplayName}}"
>{{ctx.Locale.Tr "org.members.leave"}}</button>
{{else if $.IsOrganizationOwner}}
<form>
<button class="ui red tiny button delete-button" data-modal-id="remove-organization-member"
data-url="{{$.OrgLink}}/members/action/remove" data-datauid="{{.ID}}"
data-name="{{.DisplayName}}"
data-data-organization-name="{{$.Org.DisplayName}}">{{ctx.Locale.Tr "org.members.remove"}}</button>
</form>
<button class="ui red tiny button link-action"
data-url="{{$.OrgLink}}/members/action/remove?uid={{.ID}}"
data-modal-confirm-header="{{ctx.Locale.Tr "org.members.remove"}}"
data-modal-confirm-content="{{ctx.Locale.Tr "org.members.remove.detail" .DisplayName $.Org.DisplayName}}"
>{{ctx.Locale.Tr "org.members.remove"}}</button>
{{end}}
</div>
</div>
{{end}}
</div>
{{template "base/paginate" .}}
</div>
</div>
<div class="ui g-modal-confirm delete modal" id="leave-organization">
<div class="header">
{{ctx.Locale.Tr "org.members.leave"}}
</div>
<div class="content">
<p>{{ctx.Locale.Tr "org.members.leave.detail" (HTMLFormat `<span class="%s"></span>` "dataOrganizationName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
<div class="ui g-modal-confirm delete modal" id="remove-organization-member">
<div class="header">
{{ctx.Locale.Tr "org.members.remove"}}
</div>
<div class="content">
<p>{{ctx.Locale.Tr "org.members.remove.detail" (HTMLFormat `<span class="%s"></span>` "name") (HTMLFormat `<span class="%s"></span>` "dataOrganizationName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
{{template "base/footer" .}}

View File

@ -7,9 +7,11 @@
{{template "org/team/sidebar" .}}
<div class="ui ten wide column">
{{template "org/team/navbar" .}}
{{$hasTopAttachedSegment := false}}
{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
{{if $canAddRemove}}
<div class="ui top attached segment tw-flex tw-flex-wrap tw-gap-2">
{{$hasTopAttachedSegment = true}}
<div class="ui top attached segment flex-text-block tw-flex-wrap">
<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
<div data-global-init="initSearchRepoBox" data-uid="{{.Org.ID}}" class="ui search">
<div class="ui input">
@ -24,7 +26,11 @@
</div>
</div>
{{end}}
<div class="ui{{if not $canAddRemove}} top{{end}} attached segment">
{{if $.Team.IncludesAllRepositories}}
{{$hasTopAttachedSegment = true}}
<div class="ui top attached segment">{{ctx.Locale.Tr "org.teams.all_repositories"}}</div>
{{end}}
<div class="ui {{if not $hasTopAttachedSegment}}top{{end}} attached segment">
<div class="flex-list">
{{range $.TeamRepos}}
<div class="flex-item tw-items-center">

View File

@ -26,7 +26,8 @@
</div>
{{if eq .Team.LowerName "owners"}}
<div class="item">
{{ctx.Locale.Tr "org.teams.owners_permission_desc"}}
<p>{{ctx.Locale.Tr "org.teams.owners_permission_desc"}}</p>
<p>{{ctx.Locale.Tr "org.teams.owners_permission_suggestion"}}</p>
</div>
{{else}}
<div class="item">

View File

@ -4,7 +4,8 @@
<div class="ui container">
{{template "base/alert" .}}
{{if .IsOrganizationOwner}}
<div class="flex-text-block tw-justify-end">
<div class="flex-text-block">
<div class="tw-flex-1">{{ctx.Locale.Tr "org.teams.manage_team_member_prompt"}}</div>
<a class="ui primary button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus"}} {{ctx.Locale.Tr "org.create_new_team"}}</a>
</div>
<div class="divider"></div>

View File

@ -16,6 +16,8 @@
data-locale-all-jobs="{{ctx.Locale.Tr "actions.runs.all_jobs"}}"
data-locale-triggered-via="{{ctx.Locale.Tr "actions.runs.triggered_via"}}"
data-locale-total-duration="{{ctx.Locale.Tr "actions.runs.total_duration"}}"
data-locale-run-details="{{ctx.Locale.Tr "actions.runs.run_details"}}"
data-locale-workflow-file="{{ctx.Locale.Tr "actions.runs.workflow_file"}}"
data-locale-status-unknown="{{ctx.Locale.Tr "actions.status.unknown"}}"
data-locale-status-waiting="{{ctx.Locale.Tr "actions.status.waiting"}}"
data-locale-status-running="{{ctx.Locale.Tr "actions.status.running"}}"

View File

@ -11,7 +11,7 @@
{{- end}}
</h3>
<div class="field">
<input name="commit_summary" maxlength="100" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}" autofocus>
<input name="commit_summary" maxlength="100" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}">
</div>
<div class="field">
<textarea name="commit_message" placeholder="{{ctx.Locale.Tr "repo.editor.commit_message_desc"}}" rows="5">{{.commit_message}}</textarea>

View File

@ -5,7 +5,7 @@
{{range $i, $v := .TreeNames}}
<div class="breadcrumb-divider">/</div>
{{if eq $i $l}}
<input id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr (Iif $.PageIsUpload "repo.editor.add_subdir" "repo.editor.name_your_file")}}" data-code-editor-config="{{JsonUtils.EncodeToString $.CodeEditorConfig}}" {{Iif $.PageIsUpload "" "required"}} autofocus>
<input id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr (Iif $.PageIsUpload "repo.editor.add_subdir" "repo.editor.name_your_file")}}" {{Iif $.PageIsUpload "" "required"}}>
<span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
{{else}}
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>

View File

@ -21,7 +21,7 @@
<div class="flex-text-block tw-justify-between tw-flex-wrap">
<div class="ui compact small menu small-menu-items repo-editor-menu tw-self-start">
<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
<a class="item{{if not .CodeEditorConfig.Previewable}} tw-hidden{{end}}" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
{{if not .IsNewFile}}
<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
{{end}}
@ -32,8 +32,8 @@
<div class="ui bottom attached segment tw-p-0">
<div class="ui active tab tw-rounded-b" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
data-previewable-extensions="{{StringUtils.Join $.CodeEditorConfig.PreviewableExtensions ","}}"
data-line-wrap-extensions="{{StringUtils.Join $.CodeEditorConfig.LineWrapExtensions ","}}">{{.FileContent}}</textarea>
data-code-editor-config="{{JsonUtils.EncodeToString $.CodeEditorConfig}}"
placeholder="{{ctx.Locale.Tr "editor.code_editor.placeholder"}}">{{.FileContent}}</textarea>
<div class="editor-loading is-loading"></div>
</div>
<div class="ui tab tw-px-4 tw-py-3" data-tab="preview">

View File

@ -1,7 +1,9 @@
{{$indentStyle := $.CodeEditorConfig.IndentStyle}}
{{$indentSize := or $.CodeEditorConfig.IndentSize 4}}
{{$lineWrapOn := $.CodeEditorConfig.LineWrapOn}}
{{$lineWrap := $.CodeEditorConfig.LineWrap}}
<div class="flex-text-block code-editor-options">
<button type="button" class="js-code-find ui compact mini icon button" aria-label="{{ctx.Locale.Tr "editor.code_editor.find"}}">{{svg "octicon-search"}}</button>
<button type="button" class="js-code-command-palette ui compact mini icon button" aria-label="{{ctx.Locale.Tr "editor.code_editor.command_palette"}}">{{svg "octicon-command-palette"}}</button>
<div class="native-select">
<select class="js-indent-style-select" aria-label="{{ctx.Locale.Tr "text_indent_style"}}">
<optgroup label="{{ctx.Locale.Tr "text_indent_style"}}">
@ -22,8 +24,8 @@
<div class="native-select">
<select class="js-line-wrap-select" aria-label="{{ctx.Locale.Tr "text_line_wrap_mode"}}">
<optgroup label="{{ctx.Locale.Tr "text_line_wrap_mode"}}">
<option{{if $lineWrapOn}} selected{{end}} value="on">{{ctx.Locale.Tr "text_line_wrap"}}</option>
<option{{if not $lineWrapOn}} selected{{end}} value="off">{{ctx.Locale.Tr "text_line_nowrap"}}</option>
<option{{if $lineWrap}} selected{{end}} value="on">{{ctx.Locale.Tr "text_line_wrap"}}</option>
<option{{if not $lineWrap}} selected{{end}} value="off">{{ctx.Locale.Tr "text_line_nowrap"}}</option>
</optgroup>
</select>
</div>

View File

@ -31,7 +31,9 @@
<div class="ui bottom attached segment tw-p-0">
<div class="ui active tab tw-rounded-b" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-patch"
data-context="{{.RepoLink}}">{{.FileContent}}</textarea>
data-code-editor-config="{{JsonUtils.EncodeToString $.CodeEditorConfig}}"
data-context="{{.RepoLink}}"
placeholder="{{ctx.Locale.Tr "editor.code_editor.placeholder"}}">{{.FileContent}}</textarea>
<div class="editor-loading is-loading"></div>
</div>
</div>

View File

@ -46,7 +46,7 @@
</div>
</div>
<div class="issue-content-right ui segment">
<div class="issue-content-right ui segment" data-global-init="initRepoIssueSidebar">
{{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}
{{if .PageIsComparePull}}

View File

@ -5,7 +5,7 @@
<div class="issue-sidebar-combo" data-selection-mode="single" data-update-algo="all"
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/projects?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="project_id" type="hidden" value="{{$data.SelectedProjectID}}">
<input class="combo-value" name="project_id" type="hidden" value="{{if and $pageMeta.CanModifyIssueOrPull $data.SelectedProjectIDs}}{{index $data.SelectedProjectIDs 0}}{{end}}">
<div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
<a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.projects"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}

View File

@ -66,6 +66,7 @@
</div>
{{template "repo/issue/view_content/comments" .}}
<div class="timeline-item tw-hidden" id="timeline-comments-end"></div>
{{if and .Issue.IsPull (not $.Repository.IsArchived)}}
{{template "repo/issue/view_content/pull_merge_box".}}

View File

@ -1,4 +1,4 @@
<div class="issue-content-right ui segment">
<div class="issue-content-right ui segment" data-global-init="initRepoIssueSidebar">
{{template "repo/issue/branch_selector_field" $}}{{/* TODO: RemoveIssueRef: template "repo/issue/branch_selector_field" $*/}}
{{if .Issue.IsPull}}

View File

@ -2,10 +2,10 @@
<input type="hidden" name="watch" value="{{if $.IssueWatch.IsWatching}}0{{else}}1{{end}}">
<button class="fluid ui button">
{{if $.IssueWatch.IsWatching}}
{{svg "octicon-mute" 16 "tw-mr-2"}}
{{svg "octicon-mute" 16}}
{{ctx.Locale.Tr "repo.issues.unsubscribe"}}
{{else}}
{{svg "octicon-unmute" 16 "tw-mr-2"}}
{{svg "octicon-unmute" 16}}
{{ctx.Locale.Tr "repo.issues.subscribe"}}
{{end}}
</button>

View File

@ -2,7 +2,7 @@
<div class="repo-setting-content">
<form class="ui form" action="{{.Link}}" method="post">
<h4 class="ui top attached header flex-text-block tw-justify-between tw-flex-wrap">
{{ctx.Locale.Tr "repo.settings.githooks"}}
{{.Hook.Name}}
<div class="tw-font-normal tw-font-sans tw-text-base">
{{template "repo/editor/options" dict "CodeEditorConfig" $.CodeEditorConfig}}
</div>
@ -10,13 +10,10 @@
<div class="ui attached segment">
<p>{{ctx.Locale.Tr "repo.settings.githook_edit_desc"}}</p>
{{with .Hook}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.settings.githook_name"}}</label>
<span class="hook-filename">{{.Name}}</span>
</div>
<div class="field">
<label for="content">{{ctx.Locale.Tr "repo.settings.githook_content"}}</label>
<textarea id="content" name="content" class="tw-hidden">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea>
<textarea id="content" name="content" class="tw-hidden"
data-code-editor-config="{{JsonUtils.EncodeToString $.CodeEditorConfig}}"
placeholder="{{ctx.Locale.Tr "editor.code_editor.placeholder"}}">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea>
<div class="editor-loading is-loading"></div>
</div>
<div class="inline field">

View File

@ -14,7 +14,8 @@
<div class="item">
<span class="{{if eq .LastStatus 1}}tw-text-green{{else if eq .LastStatus 2}}tw-text-red{{else}}tw-text-text-light{{end}}">{{svg "octicon-dot-fill" 22}}</span>
<div class="gt-ellipsis tw-flex-1">
<a title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{.URL}}</a>
<a title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{or .Name (ctx.Locale.Tr "repo.settings.webhook.name_empty")}}</a>
<span class="tw-ml-2 tw-text-grey-light">{{.URL}}</span>
</div>
<a class="muted tw-p-2" href="{{$.BaseLink}}/{{.ID}}">{{svg "octicon-pencil"}}</a>
<a class="tw-text-red tw-p-2 link-action"

View File

@ -6,6 +6,12 @@
*/}}
{{$isNew := not .Webhook.ID}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.webhook.name"}}</label>
<input name="name" type="text" value="{{.Webhook.Name}}" maxlength="255">
<p class="help">{{ctx.Locale.Tr "repo.settings.webhook.name_helper"}}</p>
</div>
<div class="inline field">
<div class="ui checkbox">
<input name="active" type="checkbox" {{if or $isNew .Webhook.IsActive}}checked{{end}}>

View File

@ -5,7 +5,7 @@
<div class="flex-item-leading">
{{/* using some tw helpers is the only way to align the checkbox */}}
<div class="flex-text-inline tw-mt-[2px]">
<div class="flex-text-inline tw-mt-[3px]">
{{if $.CanWriteIssuesOrPulls}}
<input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-[14px]" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} &quot;{{.Title}}&quot;">
{{end}}

View File

@ -10362,6 +10362,7 @@
}
},
"patch": {
"description": "Pass `content_version` to enable optimistic locking on body edits.\nIf the version doesn't match the current value, the request fails with 409 Conflict.\n",
"consumes": [
"application/json"
],
@ -23462,6 +23463,11 @@
},
"x-go-name": "Events"
},
"name": {
"description": "Optional human-readable name for the webhook",
"type": "string",
"x-go-name": "Name"
},
"type": {
"type": "string",
"enum": [
@ -24728,6 +24734,11 @@
"type": "string"
},
"x-go-name": "Events"
},
"name": {
"description": "Optional human-readable name",
"type": "string",
"x-go-name": "Name"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@ -24766,6 +24777,12 @@
"type": "string",
"x-go-name": "Body"
},
"content_version": {
"description": "The current version of the issue content to detect conflicts during editing",
"type": "integer",
"format": "int64",
"x-go-name": "ContentVersion"
},
"due_date": {
"type": "string",
"format": "date-time",
@ -24938,6 +24955,12 @@
"type": "string",
"x-go-name": "Body"
},
"content_version": {
"description": "The current version of the pull request content to detect conflicts during editing",
"type": "integer",
"format": "int64",
"x-go-name": "ContentVersion"
},
"due_date": {
"type": "string",
"format": "date-time",
@ -26136,6 +26159,11 @@
"format": "int64",
"x-go-name": "ID"
},
"name": {
"description": "Optional human-readable name for the webhook",
"type": "string",
"x-go-name": "Name"
},
"type": {
"description": "The type of the webhook (e.g., gitea, slack, discord)",
"type": "string",
@ -26223,6 +26251,12 @@
"format": "int64",
"x-go-name": "Comments"
},
"content_version": {
"description": "The version of the issue content for optimistic locking",
"type": "integer",
"format": "int64",
"x-go-name": "ContentVersion"
},
"created_at": {
"type": "string",
"format": "date-time",
@ -27725,6 +27759,12 @@
"format": "int64",
"x-go-name": "Comments"
},
"content_version": {
"description": "The version of the pull request content for optimistic locking",
"type": "integer",
"format": "int64",
"x-go-name": "ContentVersion"
},
"created_at": {
"type": "string",
"format": "date-time",

View File

@ -0,0 +1,18 @@
<div id="external-login-navigator" class="tw-py-1 tw-flex tw-flex-col tw-gap-3">
{{range $provider := .OAuth2Providers}}
{{/* use QueryEscape for consistent with frontend urlQueryEscape, it is right for a path component */}}
<a class="ui button external-login-link tw-gap-3" data-require-appurl-check="true" rel="nofollow" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName | QueryEscape}}">
{{$provider.IconHTML 24}} {{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
</a>
{{end}}
{{if .EnableOpenIDSignIn}}
<a class="ui button external-login-link tw-gap-3" data-require-appurl-check="true" rel="nofollow" href="{{AppSubUrl}}/user/login/openid">
{{svg "fontawesome-openid" 24}} {{ctx.Locale.Tr "sign_in_with_provider" "OpenID"}}
</a>
{{end}}
{{if .EnableSSPI}}
<a class="ui button external-login-link tw-gap-3" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1">
{{svg "fontawesome-windows" 24}} Windows SSPI
</a>
{{end}}
</div>

View File

@ -1,24 +0,0 @@
<div id="oauth2-login-navigator" class="tw-py-1">
<div class="tw-flex tw-flex-col tw-justify-center">
<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2">
{{range $provider := .OAuth2Providers}}
<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
{{$provider.IconHTML 28}}
{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
</a>
{{end}}
{{if .EnableOpenIDSignIn}}
<a class="openid ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" href="{{AppSubUrl}}/user/login/openid">
{{svg "fontawesome-openid" 28 "tw-mr-2"}}
{{ctx.Locale.Tr "sign_in_with_provider" "OpenID"}}
</a>
{{end}}
{{if .EnableSSPI}}
<a class="ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1">
{{svg "fontawesome-windows"}}
&nbsp;SSPI
</a>
{{end}}
</div>
</div>
</div>

View File

@ -46,14 +46,13 @@
</button>
</div>
</form>
{{end}}{{/*if .EnablePasswordSignInForm*/}}
{{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}}
{{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
{{if and $showOAuth2Methods .EnablePasswordSignInForm}}
{{end}}{{/*end if .EnablePasswordSignInForm*/}}
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
{{if and $showExternalAuthMethods .EnablePasswordSignInForm}}
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
{{end}}
{{if $showOAuth2Methods}}
{{template "user/auth/oauth_container" .}}
{{if $showExternalAuthMethods}}
{{template "user/auth/external_auth_methods" .}}
{{end}}
</div>
</div>

View File

@ -49,12 +49,10 @@
</button>
</div>
{{end}}
{{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}}
{{/* TODO: it seems that "EnableSSPI" is only set in "sign-in" handlers, but it should use the same logic to control its display */}}
{{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
{{if $showOAuth2Methods}}
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
{{if $showExternalAuthMethods}}
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
{{template "user/auth/oauth_container" .}}
{{template "user/auth/external_auth_methods" .}}
{{end}}
</form>
</div>

Some files were not shown because too many files have changed in this diff Show More