0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-09 05:37:47 +02:00

Merge branch 'main' into lunny/support_update_branch_api

This commit is contained in:
Lunny Xiao 2025-12-01 20:20:08 -08:00
commit d999e23d10
142 changed files with 2931 additions and 2235 deletions

View File

@ -4,7 +4,7 @@ tmp_dir = ".air"
[build]
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
cmd = "make --no-print-directory backend"
bin = "gitea"
entrypoint = ["./gitea"]
delay = 2000
include_ext = ["go", "tmpl"]
include_file = ["main.go"]

View File

@ -25,6 +25,10 @@ insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl]
indent_style = space
[templates/shared/actions/runner_badge_*.tmpl]
# editconfig lint requires these XML-like files to have charset defined, but the files don't have.
charset = unset
[Makefile]
indent_style = tab

View File

@ -1,35 +0,0 @@
name: e2e-tests
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
test-e2e:
# the "test-e2e" won't pass, and it seems that there is no useful test, so skip
# if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
if: false
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with:
node-version: 24
- run: make deps-frontend frontend deps-backend
- run: pnpm exec playwright install --with-deps
- run: make test-e2e-sqlite
timeout-minutes: 40
env:
USE_REPO_TEST_DIR: 1

View File

@ -114,6 +114,10 @@ linters:
- stringsbuilder
perfsprint:
concat-loop: false
govet:
enable:
- nilness
- unusedwrite
exclusions:
generated: lax
presets:

View File

@ -166,19 +166,19 @@ Here's how to run the test suite:
- code lint
| | |
| :-------------------- | :---------------------------------------------------------------- |
| | |
| :-------------------- | :--------------------------------------------------------------------------- |
|``make lint`` | lint everything (not needed if you only change the front- **or** backend) |
|``make lint-frontend`` | lint frontend files |
|``make lint-backend`` | lint backend files |
|``make lint-frontend`` | lint frontend files |
|``make lint-backend`` | lint backend files |
- run tests (we suggest running them on Linux)
| Command | Action | |
| :------------------------------------- | :----------------------------------------------- | ------------ |
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|``make test-sqlite[\#SpecificTestName]``| run [integration](tests/integration) test(s) for SQLite |[More details](tests/integration/README.md) |
|``make test-e2e-sqlite[\#SpecificTestName]``| run [end-to-end](tests/e2e) test(s) for SQLite |[More details](tests/e2e/README.md) |
| Command | Action | |
| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- |
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|``make test-sqlite[\#SpecificTestName]`` | run [integration](tests/integration) test(s) for SQLite | [More details](tests/integration/README.md) |
|``make test-e2e-sqlite[\#SpecificTestName]`` | run [end-to-end](tests/e2e) test(s) for SQLite | [More details](tests/e2e/README.md) |
## Translation

View File

@ -39,8 +39,7 @@ SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.20.0
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.9
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@ -333,7 +332,7 @@ lint-frontend: lint-js lint-css ## lint frontend files
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
.PHONY: lint-backend
lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
lint-backend: lint-go lint-go-gitea-vet lint-editorconfig ## lint backend files
.PHONY: lint-backend-fix
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
@ -364,6 +363,10 @@ lint-swagger: node_modules ## lint swagger files
lint-md: node_modules ## lint markdown files
$(NODE_VARS) 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
.PHONY: lint-spell
lint-spell: ## lint spelling
@go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error $(SPELLCHECK_FILES)
@ -392,11 +395,6 @@ lint-go-gitea-vet: ## lint go files with gitea-vet
@echo "Running gitea-vet..."
@$(GO) vet -vettool="$(shell GOOS= GOARCH= go tool -n gitea-vet)" ./...
.PHONY: lint-go-gopls
lint-go-gopls: ## lint go files with gopls
@echo "Running gopls check..."
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
.PHONY: lint-editorconfig
lint-editorconfig:
@echo "Running editorconfig check..."
@ -840,7 +838,6 @@ deps-tools: ## install tool dependencies
$(GO) install $(GO_LICENSES_PACKAGE) & \
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
wait
node_modules: pnpm-lock.yaml

View File

@ -2334,7 +2334,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Resynchronize pre-receive, update and post-receive hooks of all repositories.
;; Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[cron.resync_all_hooks]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -3,7 +3,6 @@ import comments from '@eslint-community/eslint-plugin-eslint-comments';
import github from 'eslint-plugin-github';
import globals from 'globals';
import importPlugin from 'eslint-plugin-import-x';
import noUseExtendNative from 'eslint-plugin-no-use-extend-native';
import playwright from 'eslint-plugin-playwright';
import regexp from 'eslint-plugin-regexp';
import sonarjs from 'eslint-plugin-sonarjs';
@ -58,7 +57,6 @@ export default defineConfig([
'array-func': arrayFunc,
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
'import-x': importPlugin,
'no-use-extend-native': noUseExtendNative,
regexp,
sonarjs,
unicorn,
@ -155,7 +153,7 @@ export default defineConfig([
'@typescript-eslint/ban-tslint-comment': [0],
'@typescript-eslint/class-literal-property-style': [0],
'@typescript-eslint/class-methods-use-this': [0],
'@typescript-eslint/consistent-generic-constructors': [0],
'@typescript-eslint/consistent-generic-constructors': [2, 'constructor'],
'@typescript-eslint/consistent-indexed-object-style': [0],
'@typescript-eslint/consistent-return': [0],
'@typescript-eslint/consistent-type-assertions': [2, {assertionStyle: 'as', objectLiteralTypeAssertions: 'allow'}],
@ -207,7 +205,7 @@ export default defineConfig([
'@typescript-eslint/no-non-null-asserted-optional-chain': [2],
'@typescript-eslint/no-non-null-assertion': [0],
'@typescript-eslint/no-redeclare': [0],
'@typescript-eslint/no-redundant-type-constituents': [2],
'@typescript-eslint/no-redundant-type-constituents': [0], // rule does not properly work without strickNullChecks
'@typescript-eslint/no-require-imports': [2],
'@typescript-eslint/no-restricted-imports': [0],
'@typescript-eslint/no-restricted-types': [0],
@ -231,6 +229,7 @@ export default defineConfig([
'@typescript-eslint/no-unsafe-return': [0],
'@typescript-eslint/no-unsafe-unary-minus': [2],
'@typescript-eslint/no-unused-expressions': [0],
'@typescript-eslint/no-unused-private-class-members': [2],
'@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}],
'@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}],
'@typescript-eslint/no-useless-constructor': [0],
@ -587,10 +586,9 @@ export default defineConfig([
'no-unsafe-negation': [2],
'no-unused-expressions': [2],
'no-unused-labels': [2],
'no-unused-private-class-members': [2],
'no-unused-private-class-members': [0], // handled by @typescript-eslint/no-unused-private-class-members
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars
'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define
'no-use-extend-native/no-use-extend-native': [2],
'no-useless-assignment': [2],
'no-useless-backreference': [2],
'no-useless-call': [2],
@ -935,7 +933,6 @@ export default defineConfig([
},
{
files: ['**/*.test.ts', 'web_src/js/test/setup.ts'],
// @ts-expect-error - https://github.com/vitest-dev/eslint-plugin-vitest/issues/737
plugins: {vitest},
languageOptions: {globals: globals.vitest},
rules: {

16
go.mod
View File

@ -17,7 +17,7 @@ require (
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15
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
@ -117,13 +117,13 @@ require (
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
golang.org/x/crypto v0.43.0
golang.org/x/crypto v0.45.0
golang.org/x/image v0.30.0
golang.org/x/net v0.45.0
golang.org/x/net v0.47.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.17.0
golang.org/x/sys v0.37.0
golang.org/x/text v0.30.0
golang.org/x/sync v0.18.0
golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8
gopkg.in/ini.v1 v1.67.0
@ -281,9 +281,9 @@ require (
go.uber.org/zap/exp v0.3.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.37.0 // indirect
golang.org/x/tools v0.38.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

36
go.sum
View File

@ -41,8 +41,8 @@ gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15 h1:qFYmz05u/s9664o7+XEgrlHXSPQ4uHO8/ccZGUb1uxA=
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e h1:4bugwPyGMLvblEm3pZ8fZProSPVxE4l0UXF2Kv6IJoY=
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e/go.mod h1:KDvcfMUoXfATPHs2mbMoXFTXT45/FAFAS39waz9tPk0=
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
@ -840,8 +840,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -878,8 +878,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -908,8 +908,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -932,8 +932,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -975,8 +975,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -987,8 +987,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1002,8 +1002,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
@ -1039,8 +1039,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -73,18 +73,18 @@ func GetReviewState(ctx context.Context, userID, pullID int64, commitSHA string)
// UpdateReviewState updates the given review inside the database, regardless of whether it existed before or not
// The given map of files with their viewed state will be merged with the previous review, if present
func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA string, updatedFiles map[string]ViewedState) error {
func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA string, updatedFiles map[string]ViewedState) (*ReviewState, error) {
log.Trace("Updating review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, updatedFiles)
review, exists, err := GetReviewState(ctx, userID, pullID, commitSHA)
if err != nil {
return err
return nil, err
}
if exists {
review.UpdatedFiles = mergeFiles(review.UpdatedFiles, updatedFiles)
} else if previousReview, err := getNewestReviewStateApartFrom(ctx, userID, pullID, commitSHA); err != nil {
return err
return nil, err
// Overwrite the viewed files of the previous review if present
} else if previousReview != nil {
@ -98,11 +98,11 @@ func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA stri
if !exists {
log.Trace("Inserting new review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, review.UpdatedFiles)
_, err := engine.Insert(review)
return err
return nil, err
}
log.Trace("Updating already existing review with ID %d (user %d, repo %d, commit %s) with the updated files %v.", review.ID, userID, pullID, commitSHA, review.UpdatedFiles)
_, err = engine.ID(review.ID).Update(&ReviewState{UpdatedFiles: review.UpdatedFiles})
return err
_, err = engine.ID(review.ID).Cols("updated_files").Update(review)
return review, err
}
// mergeFiles merges the given maps of files with their viewing state into one map.

View File

@ -22,7 +22,7 @@ var WebAuthn *webauthn.WebAuthn
// Init initializes the WebAuthn instance from the config.
func Init() {
gob.Register(&webauthn.SessionData{})
gob.Register(&webauthn.SessionData{}) // TODO: CHI-SESSION-GOB-REGISTER.
appURL, _ := protocol.FullyQualifiedOrigin(setting.AppURL)

View File

@ -4,8 +4,13 @@
package composer
import (
"archive/tar"
"archive/zip"
"compress/bzip2"
"compress/gzip"
"errors"
"io"
"io/fs"
"path"
"regexp"
"strings"
@ -29,8 +34,10 @@ var (
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
)
// Package represents a Composer package
type Package struct {
// PackageInfo represents Composer package info
type PackageInfo struct {
Filename string
Name string
Version string
Type string
@ -44,7 +51,7 @@ type Metadata struct {
Description string `json:"description,omitempty"`
Readme string `json:"readme,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Comments Comments `json:"_comments,omitempty"`
Comments Comments `json:"_comment,omitempty"`
Homepage string `json:"homepage,omitempty"`
License Licenses `json:"license,omitempty"`
Authors []Author `json:"authors,omitempty"`
@ -75,7 +82,7 @@ func (l *Licenses) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &values); err != nil {
return err
}
*l = Licenses(values)
*l = values
}
return nil
}
@ -97,7 +104,7 @@ func (c *Comments) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &values); err != nil {
return err
}
*c = Comments(values)
*c = values
}
return nil
}
@ -111,39 +118,121 @@ type Author struct {
var nameMatch = regexp.MustCompile(`\A[a-z0-9]([_\.-]?[a-z0-9]+)*/[a-z0-9](([_\.]?|-{0,2})[a-z0-9]+)*\z`)
// ParsePackage parses the metadata of a Composer package file
func ParsePackage(r io.ReaderAt, size int64) (*Package, error) {
archive, err := zip.NewReader(r, size)
type ReadSeekAt interface {
io.Reader
io.ReaderAt
io.Seeker
Size() int64
}
func readPackageFileZip(r ReadSeekAt, filename string, limit int) ([]byte, error) {
archive, err := zip.NewReader(r, r.Size())
if err != nil {
return nil, err
}
for _, file := range archive.File {
if strings.Count(file.Name, "/") > 1 {
continue
}
if strings.HasSuffix(strings.ToLower(file.Name), "composer.json") {
filePath := path.Clean(file.Name)
if util.AsciiEqualFold(filePath, filename) {
f, err := archive.Open(file.Name)
if err != nil {
return nil, err
}
defer f.Close()
return ParseComposerFile(archive, path.Dir(file.Name), f)
return util.ReadWithLimit(f, limit)
}
}
return nil, ErrMissingComposerFile
return nil, fs.ErrNotExist
}
// ParseComposerFile parses a composer.json file to retrieve the metadata of a Composer package
func ParseComposerFile(archive *zip.Reader, pathPrefix string, r io.Reader) (*Package, error) {
func readPackageFileTar(r io.Reader, filename string, limit int) ([]byte, error) {
tarReader := tar.NewReader(r)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
filePath := path.Clean(header.Name)
if util.AsciiEqualFold(filePath, filename) {
return util.ReadWithLimit(tarReader, limit)
}
}
return nil, fs.ErrNotExist
}
const (
pkgExtZip = ".zip"
pkgExtTarGz = ".tar.gz"
pkgExtTarBz2 = ".tar.bz2"
)
func detectPackageExtName(r ReadSeekAt) (string, error) {
headBytes := make([]byte, 4)
_, err := r.ReadAt(headBytes, 0)
if err != nil {
return "", err
}
_, err = r.Seek(0, io.SeekStart)
if err != nil {
return "", err
}
switch {
case headBytes[0] == 'P' && headBytes[1] == 'K':
return pkgExtZip, nil
case string(headBytes[:3]) == "BZh":
return pkgExtTarBz2, nil
case headBytes[0] == 0x1f && headBytes[1] == 0x8b:
return pkgExtTarGz, nil
}
return "", util.NewInvalidArgumentErrorf("not a valid package file")
}
func readPackageFile(pkgExt string, r ReadSeekAt, filename string, limit int) ([]byte, error) {
_, err := r.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
switch pkgExt {
case pkgExtZip:
return readPackageFileZip(r, filename, limit)
case pkgExtTarBz2:
bzip2Reader := bzip2.NewReader(r)
return readPackageFileTar(bzip2Reader, filename, limit)
case pkgExtTarGz:
gzReader, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
return readPackageFileTar(gzReader, filename, limit)
}
return nil, util.NewInvalidArgumentErrorf("not a valid package file")
}
// ParsePackage parses the metadata of a Composer package file
func ParsePackage(r ReadSeekAt, optVersion ...string) (*PackageInfo, error) {
pkgExt, err := detectPackageExtName(r)
if err != nil {
return nil, err
}
dataComposerJSON, err := readPackageFile(pkgExt, r, "composer.json", 10*1024*1024)
if errors.Is(err, fs.ErrNotExist) {
return nil, ErrMissingComposerFile
} else if err != nil {
return nil, err
}
var cj struct {
Name string `json:"name"`
Version string `json:"version"`
Type string `json:"type"`
Metadata
}
if err := json.NewDecoder(r).Decode(&cj); err != nil {
if err := json.Unmarshal(dataComposerJSON, &cj); err != nil {
return nil, err
}
@ -151,6 +240,9 @@ func ParseComposerFile(archive *zip.Reader, pathPrefix string, r io.Reader) (*Pa
return nil, ErrInvalidName
}
if cj.Version == "" {
cj.Version = util.OptionalArg(optVersion)
}
if cj.Version != "" {
if _, err := version.NewSemver(cj.Version); err != nil {
return nil, ErrInvalidVersion
@ -168,17 +260,23 @@ func ParseComposerFile(archive *zip.Reader, pathPrefix string, r io.Reader) (*Pa
if cj.Readme == "" {
cj.Readme = "README.md"
}
f, err := archive.Open(path.Join(pathPrefix, cj.Readme))
if err == nil {
// 10kb limit for readme content
buf, _ := io.ReadAll(io.LimitReader(f, 10*1024))
cj.Readme = string(buf)
_ = f.Close()
} else {
dataReadmeMd, _ := readPackageFile(pkgExt, r, cj.Readme, 10*1024)
// FIXME: legacy problem, the "Readme" field is abused, it should always be the path to the readme file
if len(dataReadmeMd) == 0 {
cj.Readme = ""
} else {
cj.Readme = string(dataReadmeMd)
}
return &Package{
// FIXME: legacy format: strings.ToLower(fmt.Sprintf("%s.%s.zip", strings.ReplaceAll(cp.Name, "/", "-"), cp.Version)), doesn't read good
pkgFilename := strings.ReplaceAll(cj.Name, "/", "-")
if cj.Version != "" {
pkgFilename += "." + cj.Version
}
pkgFilename += pkgExt
return &PackageInfo{
Filename: pkgFilename,
Name: cj.Name,
Version: cj.Version,
Type: cj.Type,

View File

@ -4,14 +4,19 @@
package composer
import (
"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"io"
"strings"
"testing"
"code.gitea.io/gitea/modules/json"
"github.com/dsnet/compress/bzip2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
@ -26,8 +31,10 @@ const (
license = "MIT"
)
const composerContent = `{
func buildComposerContent(version string) string {
return `{
"name": "` + name + `",
"version": "` + version + `",
"description": "` + description + `",
"type": "` + packageType + `",
"license": "` + license + `",
@ -44,8 +51,9 @@ const composerContent = `{
"require": {
"php": ">=7.2 || ^8.0"
},
"_comments": "` + comments + `"
"_comment": "` + comments + `"
}`
}
func TestLicenseUnmarshal(t *testing.T) {
var l Licenses
@ -73,16 +81,34 @@ func TestParsePackage(t *testing.T) {
archive := zip.NewWriter(&buf)
for name, content := range files {
w, _ := archive.Create(name)
w.Write([]byte(content))
_, _ = w.Write([]byte(content))
}
archive.Close()
_ = archive.Close()
return buf.Bytes()
}
createArchiveTar := func(comp func(io.Writer) io.WriteCloser, files map[string]string) []byte {
var buf bytes.Buffer
w := comp(&buf)
archive := tar.NewWriter(w)
for name, content := range files {
hdr := &tar.Header{
Name: name,
Mode: 0o600,
Size: int64(len(content)),
}
_ = archive.WriteHeader(hdr)
_, _ = archive.Write([]byte(content))
}
_ = w.Close()
_ = archive.Close()
return buf.Bytes()
}
t.Run("MissingComposerFile", func(t *testing.T) {
data := createArchive(map[string]string{"dummy.txt": ""})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.Nil(t, cp)
assert.ErrorIs(t, err, ErrMissingComposerFile)
})
@ -90,7 +116,7 @@ func TestParsePackage(t *testing.T) {
t.Run("MissingComposerFileInRoot", func(t *testing.T) {
data := createArchive(map[string]string{"sub/sub/composer.json": ""})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.Nil(t, cp)
assert.ErrorIs(t, err, ErrMissingComposerFile)
})
@ -98,7 +124,7 @@ func TestParsePackage(t *testing.T) {
t.Run("InvalidComposerFile", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": ""})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.Nil(t, cp)
assert.Error(t, err)
})
@ -106,7 +132,7 @@ func TestParsePackage(t *testing.T) {
t.Run("InvalidPackageName", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": "{}"})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.Nil(t, cp)
assert.ErrorIs(t, err, ErrInvalidName)
})
@ -114,7 +140,7 @@ func TestParsePackage(t *testing.T) {
t.Run("InvalidPackageVersion", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": `{"name": "gitea/composer-package", "version": "1.a.3"}`})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.Nil(t, cp)
assert.ErrorIs(t, err, ErrInvalidVersion)
})
@ -122,22 +148,21 @@ func TestParsePackage(t *testing.T) {
t.Run("InvalidReadmePath", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": `{"name": "gitea/composer-package", "readme": "sub/README.md"}`})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
cp, err := ParsePackage(bytes.NewReader(data))
assert.NoError(t, err)
assert.NotNil(t, cp)
assert.Empty(t, cp.Metadata.Readme)
})
t.Run("Valid", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": composerContent, "README.md": readme})
cp, err := ParsePackage(bytes.NewReader(data), int64(len(data)))
assert.NoError(t, err)
assertValidPackage := func(t *testing.T, data []byte, version, filename string) {
cp, err := ParsePackage(bytes.NewReader(data))
require.NoError(t, err)
assert.NotNil(t, cp)
assert.Equal(t, filename, cp.Filename)
assert.Equal(t, name, cp.Name)
assert.Empty(t, cp.Version)
assert.Equal(t, version, cp.Version)
assert.Equal(t, description, cp.Metadata.Description)
assert.Equal(t, readme, cp.Metadata.Readme)
assert.Len(t, cp.Metadata.Comments, 1)
@ -149,5 +174,25 @@ func TestParsePackage(t *testing.T) {
assert.Equal(t, packageType, cp.Type)
assert.Len(t, cp.Metadata.License, 1)
assert.Equal(t, license, cp.Metadata.License[0])
}
t.Run("ValidZip", func(t *testing.T) {
data := createArchive(map[string]string{"composer.json": buildComposerContent(""), "README.md": readme})
assertValidPackage(t, data, "", "gitea-composer-package.zip")
})
t.Run("ValidTarBz2", func(t *testing.T) {
data := createArchiveTar(func(w io.Writer) io.WriteCloser {
bz2Writer, _ := bzip2.NewWriter(w, nil)
return bz2Writer
}, map[string]string{"composer.json": buildComposerContent("1.0"), "README.md": readme})
assertValidPackage(t, data, "1.0", "gitea-composer-package.1.0.tar.bz2")
})
t.Run("ValidTarGz", func(t *testing.T) {
data := createArchiveTar(func(w io.Writer) io.WriteCloser {
return gzip.NewWriter(w)
}, map[string]string{"composer.json": buildComposerContent(""), "README.md": readme})
assertValidPackage(t, data, "", "gitea-composer-package.tar.gz")
})
}

View File

@ -592,10 +592,10 @@
".settings": "folder-config",
"_settings": "folder-config",
"__settings__": "folder-config",
"META-INF": "folder-config",
".META-INF": "folder-config",
"_META-INF": "folder-config",
"__META-INF__": "folder-config",
"meta-inf": "folder-config",
".meta-inf": "folder-config",
"_meta-inf": "folder-config",
"__meta-inf__": "folder-config",
"option": "folder-config",
".option": "folder-config",
"_option": "folder-config",
@ -2196,14 +2196,14 @@
".templates": "folder-template",
"_templates": "folder-template",
"__templates__": "folder-template",
"github/ISSUE_TEMPLATE": "folder-template",
".github/ISSUE_TEMPLATE": "folder-template",
"_github/ISSUE_TEMPLATE": "folder-template",
"__github/ISSUE_TEMPLATE__": "folder-template",
"github/PULL_REQUEST_TEMPLATE": "folder-template",
".github/PULL_REQUEST_TEMPLATE": "folder-template",
"_github/PULL_REQUEST_TEMPLATE": "folder-template",
"__github/PULL_REQUEST_TEMPLATE__": "folder-template",
"github/issue_template": "folder-template",
".github/issue_template": "folder-template",
"_github/issue_template": "folder-template",
"__github/issue_template__": "folder-template",
"github/pull_request_template": "folder-template",
".github/pull_request_template": "folder-template",
"_github/pull_request_template": "folder-template",
"__github/pull_request_template__": "folder-template",
"util": "folder-utils",
".util": "folder-utils",
"_util": "folder-utils",
@ -2328,22 +2328,22 @@
".osx": "folder-macos",
"_osx": "folder-macos",
"__osx__": "folder-macos",
"DS_Store": "folder-macos",
".DS_Store": "folder-macos",
"_DS_Store": "folder-macos",
"__DS_Store__": "folder-macos",
"iPhone": "folder-macos",
".iPhone": "folder-macos",
"_iPhone": "folder-macos",
"__iPhone__": "folder-macos",
"iPad": "folder-macos",
".iPad": "folder-macos",
"_iPad": "folder-macos",
"__iPad__": "folder-macos",
"iPod": "folder-macos",
".iPod": "folder-macos",
"_iPod": "folder-macos",
"__iPod__": "folder-macos",
"ds_store": "folder-macos",
".ds_store": "folder-macos",
"_ds_store": "folder-macos",
"__ds_store__": "folder-macos",
"iphone": "folder-macos",
".iphone": "folder-macos",
"_iphone": "folder-macos",
"__iphone__": "folder-macos",
"ipad": "folder-macos",
".ipad": "folder-macos",
"_ipad": "folder-macos",
"__ipad__": "folder-macos",
"ipod": "folder-macos",
".ipod": "folder-macos",
"_ipod": "folder-macos",
"__ipod__": "folder-macos",
"macbook": "folder-macos",
".macbook": "folder-macos",
"_macbook": "folder-macos",
@ -3219,6 +3219,22 @@
".favicons": "folder-favicon",
"_favicons": "folder-favicon",
"__favicons__": "folder-favicon",
"feature": "folder-features",
".feature": "folder-features",
"_feature": "folder-features",
"__feature__": "folder-features",
"features": "folder-features",
".features": "folder-features",
"_features": "folder-features",
"__features__": "folder-features",
"feat": "folder-features",
".feat": "folder-features",
"_feat": "folder-features",
"__feat__": "folder-features",
"feats": "folder-features",
".feats": "folder-features",
"_feats": "folder-features",
"__feats__": "folder-features",
"lefthook": "folder-lefthook",
".lefthook": "folder-lefthook",
"_lefthook": "folder-lefthook",
@ -3475,34 +3491,14 @@
".cues": "folder-cue",
"_cues": "folder-cue",
"__cues__": "folder-cue",
"meta-inf": "folder-config",
".meta-inf": "folder-config",
"_meta-inf": "folder-config",
"__meta-inf__": "folder-config",
"github/issue_template": "folder-template",
".github/issue_template": "folder-template",
"_github/issue_template": "folder-template",
"__github/issue_template__": "folder-template",
"github/pull_request_template": "folder-template",
".github/pull_request_template": "folder-template",
"_github/pull_request_template": "folder-template",
"__github/pull_request_template__": "folder-template",
"ds_store": "folder-macos",
".ds_store": "folder-macos",
"_ds_store": "folder-macos",
"__ds_store__": "folder-macos",
"iphone": "folder-macos",
".iphone": "folder-macos",
"_iphone": "folder-macos",
"__iphone__": "folder-macos",
"ipad": "folder-macos",
".ipad": "folder-macos",
"_ipad": "folder-macos",
"__ipad__": "folder-macos",
"ipod": "folder-macos",
".ipod": "folder-macos",
"_ipod": "folder-macos",
"__ipod__": "folder-macos"
"license": "folder-license",
".license": "folder-license",
"_license": "folder-license",
"__license__": "folder-license",
"licenses": "folder-license",
".licenses": "folder-license",
"_licenses": "folder-license",
"__licenses__": "folder-license"
},
"folderNamesExpanded": {
"rust": "folder-rust-open",
@ -6724,6 +6720,22 @@
".favicons": "folder-favicon-open",
"_favicons": "folder-favicon-open",
"__favicons__": "folder-favicon-open",
"feature": "folder-features-open",
".feature": "folder-features-open",
"_feature": "folder-features-open",
"__feature__": "folder-features-open",
"features": "folder-features-open",
".features": "folder-features-open",
"_features": "folder-features-open",
"__features__": "folder-features-open",
"feat": "folder-features-open",
".feat": "folder-features-open",
"_feat": "folder-features-open",
"__feat__": "folder-features-open",
"feats": "folder-features-open",
".feats": "folder-features-open",
"_feats": "folder-features-open",
"__feats__": "folder-features-open",
"lefthook": "folder-lefthook-open",
".lefthook": "folder-lefthook-open",
"_lefthook": "folder-lefthook-open",
@ -6979,7 +6991,15 @@
"cues": "folder-cue-open",
".cues": "folder-cue-open",
"_cues": "folder-cue-open",
"__cues__": "folder-cue-open"
"__cues__": "folder-cue-open",
"license": "folder-license-open",
".license": "folder-license-open",
"_license": "folder-license-open",
"__license__": "folder-license-open",
"licenses": "folder-license-open",
".licenses": "folder-license-open",
"_licenses": "folder-license-open",
"__licenses__": "folder-license-open"
},
"rootFolderNames": {},
"rootFolderNamesExpanded": {},
@ -7021,7 +7041,7 @@
"twee": "twine",
"yml.dist": "yaml",
"yaml.dist": "yaml",
"YAML-tmLanguage": "yaml",
"yaml-tmlanguage": "yaml",
"xml": "xml",
"plist": "xml",
"xsd": "xml",
@ -7031,7 +7051,7 @@
"resx": "xml",
"iml": "xml",
"xquery": "xml",
"tmLanguage": "xml",
"tmlanguage": "xml",
"manifest": "xml",
"project": "xml",
"xml.dist": "xml",
@ -7130,6 +7150,8 @@
"srf": "image",
"srw": "image",
"x3f": "image",
"ktx": "image",
"ktx2": "image",
"pal": "palette",
"gpl": "palette",
"act": "palette",
@ -7315,6 +7337,7 @@
"cp": "cpp",
"mii": "cpp",
"ii": "cpp",
"cppm": "cpp",
"hh": "hpp",
"hpp": "hpp",
"hxx": "hpp",
@ -7401,6 +7424,11 @@
"fsi": "fsharp",
"fsproj": "fsharp",
"swift": "swift",
"xcplayground": "swift",
"swiftdeps": "swift",
"swiftdoc": "swift",
"swiftmodule": "swift",
"swiftsourceinfo": "swift",
"ino": "arduino",
"dockerignore": "docker",
"dockerfile": "docker",
@ -8133,12 +8161,10 @@
"css.jsx": "vanilla-extract",
"toc": "toc",
"cue": "cue",
"yaml-tmlanguage": "yaml",
"tmlanguage": "xml",
"lean": "lean",
"cljx": "clojure",
"clojure": "clojure",
"edn": "clojure",
"cppm": "cpp",
"ccm": "cpp",
"cxxm": "cpp",
"c++m": "cpp",
@ -8442,36 +8468,36 @@
"gradlew": "gradle",
"gradle-wrapper.properties": "gradle",
"gradlew.bat": "gradle",
"copying": "certificate",
"copying.md": "certificate",
"copying.rst": "certificate",
"copying.txt": "certificate",
"copyright": "certificate",
"copyright.md": "certificate",
"copyright.rst": "certificate",
"copyright.txt": "certificate",
"license": "certificate",
"license-agpl": "certificate",
"license-apache": "certificate",
"license-bsd": "certificate",
"license-mit": "certificate",
"license-gpl": "certificate",
"license-lgpl": "certificate",
"license.md": "certificate",
"license.rst": "certificate",
"license.txt": "certificate",
"licence": "certificate",
"licence-agpl": "certificate",
"licence-apache": "certificate",
"licence-bsd": "certificate",
"licence-mit": "certificate",
"licence-gpl": "certificate",
"licence-lgpl": "certificate",
"licence.md": "certificate",
"licence.rst": "certificate",
"licence.txt": "certificate",
"unlicense": "certificate",
"unlicense.txt": "certificate",
"copying": "license",
"copying.md": "license",
"copying.rst": "license",
"copying.txt": "license",
"copyright": "license",
"copyright.md": "license",
"copyright.rst": "license",
"copyright.txt": "license",
"license": "license",
"license-agpl": "license",
"license-apache": "license",
"license-bsd": "license",
"license-mit": "license",
"license-gpl": "license",
"license-lgpl": "license",
"license.md": "license",
"license.rst": "license",
"license.txt": "license",
"licence": "license",
"licence-agpl": "license",
"licence-apache": "license",
"licence-bsd": "license",
"licence-mit": "license",
"licence-gpl": "license",
"licence-lgpl": "license",
"licence.md": "license",
"licence.rst": "license",
"licence.txt": "license",
"unlicense": "unlicense",
"unlicense.txt": "unlicense",
".htpasswd": "key",
"sha256sums": "key",
".secrets": "key",
@ -8487,6 +8513,7 @@
".rspec": "rspec",
".swift-format": "swift",
".swift-version": "swift",
".swiftformat": "swift",
"dockerfile": "docker",
"dockerfile.prod": "docker",
"dockerfile.production": "docker",
@ -8624,7 +8651,7 @@
"graphql.config.mts": "graphql",
"graphql.config.cts": "graphql",
".graphqlconfig": "graphql",
"XamlStyler.json": "xaml",
"xamlstyler.json": "xaml",
".happo.js": "happo",
".happo.mjs": "happo",
".happo.cjs": "happo",
@ -8648,11 +8675,11 @@
".git-for-windows-updater": "git",
"git-history": "git",
".luacheckrc": "lua",
".Rhistory": "r",
".rhistory": "r",
".pubignore": "dart",
"cmakelists.txt": "cmake",
"cmakecache.txt": "cmake",
"CMakePresets.json": "cmake",
"cmakepresets.json": "cmake",
"semgrep.yml": "semgrep",
".semgrepignore": "semgrep",
"vue.config.js": "vue-config",
@ -8783,7 +8810,7 @@
"cabal.project": "cabal",
"cabal.project.freeze": "cabal",
"cabal.project.local": "cabal",
"CNAME": "http",
"cname": "http",
"project.graphcool": "graphcool",
"webpack.base.js": "webpack",
"webpack.base.mjs": "webpack",
@ -9023,6 +9050,12 @@
"rspress.config.ts": "rstack",
"rslint.json": "rstack",
"rslint.jsonc": "rstack",
"lynx.config.js": "lynx",
"lynx.config.mjs": "lynx",
"lynx.config.cjs": "lynx",
"lynx.config.ts": "lynx",
"lynx.config.mts": "lynx",
"lynx.config.cts": "lynx",
"ionic.config.json": "ionic",
".io-config.json": "ionic",
"gulpfile.js": "gulp",
@ -9215,7 +9248,7 @@
"sonar-project.properties": "sonarcloud",
".sonarcloud.properties": "sonarcloud",
"sonarcloud.yaml": "sonarcloud",
"SonarQube.Analysis.xml": "sonarcloud",
"sonarqube.analysis.xml": "sonarcloud",
"protractor.conf.js": "protractor",
"protractor.conf.ts": "protractor",
"protractor.conf.coffee": "protractor",
@ -9577,7 +9610,7 @@
".gitpod.yml": "gitpod",
".stackblitzrc": "stackblitz",
"codeowners": "codeowners",
"OWNERS": "codeowners",
"owners": "codeowners",
".gcloudignore": "gcp",
"amplify.yml": "amplify",
".huskyrc": "husky",
@ -9752,6 +9785,18 @@
"vitest.config.ts": "vitest",
"vitest.config.mts": "vitest",
"vitest.config.cts": "vitest",
"vitest.unit.config.js": "vitest",
"vitest.unit.config.mjs": "vitest",
"vitest.unit.config.cjs": "vitest",
"vitest.unit.config.ts": "vitest",
"vitest.unit.config.mts": "vitest",
"vitest.unit.config.cts": "vitest",
"vitest.e2e.config.js": "vitest",
"vitest.e2e.config.mjs": "vitest",
"vitest.e2e.config.cjs": "vitest",
"vitest.e2e.config.ts": "vitest",
"vitest.e2e.config.mts": "vitest",
"vitest.e2e.config.cts": "vitest",
"velite.config.js": "velite",
"velite.config.mjs": "velite",
"velite.config.cjs": "velite",
@ -9939,7 +9984,7 @@
"steadybit.yml": "steadybit",
".steadybit.yaml": "steadybit",
"steadybit.yaml": "steadybit",
"Caddyfile": "caddy",
"caddyfile": "caddy",
"openapi.json": "openapi",
"openapi.yml": "openapi",
"openapi.yaml": "openapi",
@ -10157,8 +10202,8 @@
"project.garden.yml": "garden",
"project.garden.yaml": "garden",
".gardenignore": "garden",
"PklProject": "pkl",
"PklProject.deps.json": "pkl",
"pklproject": "pkl",
"pklproject.deps.json": "pkl",
"k8s.yml": "kubernetes",
"k8s.yaml": "kubernetes",
"kubernetes.yml": "kubernetes",
@ -10199,6 +10244,7 @@
".coderabbit.yml": "coderabbit-ai",
".coderabbit.yaml": "coderabbit-ai",
".aiexclude": "gemini-ai",
"gemini.md": "gemini-ai",
"taze.config.js": "taze",
"taze.config.mjs": "taze",
"taze.config.cjs": "taze",
@ -10266,7 +10312,7 @@
".histoire.cts": "histoire",
"install": "installation",
"installation": "installation",
".github/FUNDING.yml": "github-sponsors",
".github/funding.yml": "github-sponsors",
"fabric.mod.json": "minecraft-fabric",
".umirc.js": "umi",
".umirc.mjs": "umi",
@ -10297,15 +10343,18 @@
"packship.config.mjs": "packship",
"packship.config.mts": "packship",
"packship.config.json": "packship",
"Snakefile": "snakemake",
"snakefile": "snakemake",
".hadolint.yaml": "hadolint",
".hadolint.yml": "hadolint",
"hadolint.yaml": "hadolint",
"hadolint.yml": "hadolint",
"tsdoc.json": "tsdoc",
".oxlintrc.json": "oxlint",
"CLAUDE.md": "claude",
"CLAUDE.local.md": "claude",
".oxlintrc.json": "oxc",
".oxlintrc.jsonc": "oxc",
".oxfmtrc.json": "oxc",
".oxfmtrc.jsonc": "oxc",
"claude.md": "claude",
"claude.local.md": "claude",
".cursorignore": "cursor",
".cursorindexingignore": "cursor",
".cursorrules": "cursor",
@ -10323,23 +10372,10 @@
"src/bashly-strings.yaml": "bashly-strings",
"src/bashly-strings.yml": "bashly-strings",
"google-services.json": "google",
"GoogleService-Info.plist": "google",
"googleservice-info.plist": "google",
".shellcheckrc": "shellcheck",
"shellcheckrc": "shellcheck",
"xamlstyler.json": "xaml",
".rhistory": "r",
"cmakepresets.json": "cmake",
"cname": "http",
"sonarqube.analysis.xml": "sonarcloud",
"owners": "codeowners",
"caddyfile": "caddy",
"pklproject": "pkl",
"pklproject.deps.json": "pkl",
".github/funding.yml": "github-sponsors",
"snakefile": "snakemake",
"claude.md": "claude",
"claude.local.md": "claude",
"googleservice-info.plist": "google",
"warp.md": "warp",
"language-configuration.json": "jsonc",
"icon-theme.json": "jsonc",
"color-theme.json": "jsonc",
@ -10366,6 +10402,7 @@
"toml": "toml",
"diff": "diff",
"json": "json",
"jsonl": "json",
"jsonc": "json",
"json5": "json",
"blink": "blink",
@ -10541,7 +10578,8 @@
"gnuplot": "gnuplot",
"helm": "helm",
"nginx": "nginx",
"cue": "cue"
"cue": "cue",
"lean": "lean"
},
"light": {
"fileExtensions": {
@ -10749,7 +10787,8 @@
"src/bashly-strings.yaml": "bashly-strings_light",
"src/bashly-strings.yml": "bashly-strings_light",
".shellcheckrc": "shellcheck_light",
"shellcheckrc": "shellcheck_light"
"shellcheckrc": "shellcheck_light",
"warp.md": "warp_light"
},
"languageIds": {
"toml": "toml_light",

View File

@ -92,7 +92,7 @@
"capnp": "<svg viewBox='0 0 24 24'><path fill='#c62828' d='M17 3V2h4v8h-4c-.085-2.088-.445-4.042-3-4-2.917 0-5 2.51-5 5 0 3 .495 6.981 4.67 6.981 2.906-.26 2.99-2.705 3.33-4.981h4c0 5.806-3.314 9.052-9 9-6.154-.073-8.915-4.685-9-10-.128-6.14 4.568-9.2 10.414-9.65 1.301-.028 2.466 0 3.586.65'/></svg>",
"cbx": "<svg viewBox='0 0 1024 1024'><path fill='#1565c0' d='M128 704v128c0 70.692 57.308 128 128 128h608c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M704 704v192h128V704z'/><path fill='#fff8e1' d='M192 704v96c0 53.184 42.816 96 96 96h544a96 96 0 0 1-96-96 96 96 0 0 1 96-96z'/><path fill='#ff1744' d='M320 832h192v192l-96-96-96 96z'/><path fill='#2196f3' d='M256 64c-70.692 0-128 57.308-128 128v640c0 11.088 1.557 21.787 4.207 32.047C146.767 807.565 197.672 768.07 256 768h608c17.728 0 32-14.272 32-32V96c0-17.728-14.272-32-32-32z'/><path fill='#e3f2fd' d='M384 192c-70.692 0-128 57.308-128 128 .171 67.295 52.422 122.965 119.57 127.396L256 640h80l156.748-252.488h-.146A128 128 0 0 0 512 320c0-70.692-57.308-128-128-128m320 0c-70.692 0-128 57.308-128 128 .171 67.295 52.422 122.965 119.57 127.396L576 640h80l156.748-252.488h-.146A128 128 0 0 0 832 320c0-70.692-57.308-128-128-128'/></svg>",
"cds": "<svg viewBox='0 0 16 16'><g fill='#0288d1'><rect width='4' height='1' x='7' y='9' ry='.5'/><rect width='3' height='1' x='8' y='11' ry='.5'/><rect width='4' height='1' x='7' y='13' ry='.5'/><path d='m5 9-1 1 1.5 1.5L4 13l1 1 2.5-2.5z'/><path d='M6 2a3 3 0 0 0-2.598 1.5 3 3 0 0 0-.187 2.607 3 3 0 0 0-1.514.965 3 3 0 0 0-.42 3.196A3 3 0 0 0 4 12v-1a2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 .515.076l.159-.591A2 2 0 0 1 4 5a2 2 0 0 1 2-2 2 2 0 0 1 2 2l.594.594A2 2 0 0 1 10 5a2 2 0 0 1 2 2 2 2 0 0 1 2 2 2 2 0 0 1-2 2v1a3 3 0 0 0 2.898-2.223A3 3 0 0 0 13.5 6.402a3 3 0 0 0-.63-.267 3 3 0 0 0-1.722-1.906 3 3 0 0 0-2.252-.014 3 3 0 0 0-2.119-2.113A3 3 0 0 0 6 2'/></g></svg>",
"certificate": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='M4 6v14a2 2 0 0 0 2 2h12v6l3-2 3 2v-6h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2m2 0h8v2H6Zm0 4h6v2H6Zm0 4h8v2H6Zm10 6H6v-2h10Zm8-6v4l-3-2-3 2v-4l-4-2 4-2V6l3 2 3-2v4.2l4 1.8Z'/></svg>",
"certificate": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='M2 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h6v3l2-1.25L12 14v-3h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1Zm0 1h4v1H2Zm6 0 2 1.25L12 3v2.5l2 1-2 1V10l-2-1.25L8 10V7.5l-2-1 2-1zM2 5h3v1H2Zm0 2h3v1H2Zm0 2h4v1H2Z'/></svg>",
"changelog": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#8bc34a' d='M13 3a9 9 0 0 0-9 9H1l4 4 4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52.77-1.28-3.52-2.09V8z'/></svg>",
"chess": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='M6 26h20v4H6zm16.5-13a5.49 5.49 0 0 0-4.5 2.344V10h4V6h-4V2h-4v4h-4v4h4v5.344a5.498 5.498 0 1 0-5 8.63V24h14v-.025A5.499 5.499 0 0 0 22.5 13'/></svg>",
"chess_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M6 26h20v4H6zm16.5-13a5.49 5.49 0 0 0-4.5 2.344V10h4V6h-4V2h-4v4h-4v4h4v5.344a5.498 5.498 0 1 0-5 8.63V24h14v-.025A5.499 5.499 0 0 0 22.5 13'/></svg>",
@ -359,6 +359,8 @@
"folder-fastlane": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' 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='#e3f2fd' d='m15.508 21.618 1.272 3.936a1.456 1.456 0 0 0-.06 1.922 1.35 1.35 0 0 0 .937.486l.1.003a1.33 1.33 0 0 0 .894-.346 1.4 1.4 0 0 0 .207-.231 1.446 1.446 0 0 0-.307-1.972 1.36 1.36 0 0 0-.597-.256l-1.586-5.037a.16.16 0 0 0-.092-.101.15.15 0 0 0-.134.007 1.92 1.92 0 0 1-2.059-.113 1.995 1.995 0 0 1-.423-2.72 1.84 1.84 0 0 1 2.088-.697.16.16 0 0 0 .199-.102l.325-.95a.17.17 0 0 0-.007-.128.16.16 0 0 0-.093-.084 3 3 0 0 0-.978-.167h-.039A3.215 3.215 0 0 0 12 18.296a3.3 3.3 0 0 0 1.321 2.698 3.08 3.08 0 0 0 2.187.624'/><path fill='#e3f2fd' d='M15.802 17.146a1.35 1.35 0 0 0-1.787.535 1.45 1.45 0 0 0-.157 1.074 1.4 1.4 0 0 0 .622.875 1.33 1.33 0 0 0 .706.203 1.36 1.36 0 0 0 1.176-.685 1.47 1.47 0 0 0 .177-.971l4.14-3.149a.17.17 0 0 0 .065-.122.17.17 0 0 0-.05-.13 2.09 2.09 0 0 1-.556-2.063 1.883 1.883 0 0 1 2.383-1.262 1.95 1.95 0 0 1 1.31 1.823.16.16 0 0 0 .155.161l.983.02a.2.2 0 0 0 .114-.047.17.17 0 0 0 .048-.117 3.34 3.34 0 0 0-.945-2.333A3.12 3.12 0 0 0 21.939 10a4 4 0 0 0-.293.014 3.14 3.14 0 0 0-2.162 1.182 3.4 3.4 0 0 0-.457 3.457Zm10.842 10.755a1.4 1.4 0 0 0 .736-.77 1.45 1.45 0 0 0-.005-1.083 1.38 1.38 0 0 0-.744-.763 1.3 1.3 0 0 0-1.047.005 1.4 1.4 0 0 0-.693.672l-5.12.014a.16.16 0 0 0-.123.06.17.17 0 0 0-.033.135 2.08 2.08 0 0 1-.724 2 1.82 1.82 0 0 1-1.4.355 1.86 1.86 0 0 1-1.233-.773 2 2 0 0 1-.016-2.286.17.17 0 0 0-.033-.226l-.778-.613a.15.15 0 0 0-.12-.032.16.16 0 0 0-.105.065 3.36 3.36 0 0 0-.575 2.45 3.3 3.3 0 0 0 1.266 2.153 3.1 3.1 0 0 0 1.874.634 3.15 3.15 0 0 0 2.572-1.349 3.4 3.4 0 0 0 .545-1.264l4.01-.04a1.35 1.35 0 0 0 1.746.656'/><path fill='#e3f2fd' d='m27.718 23.882 1.228-3.945a1.34 1.34 0 0 0 .824-.473 1.44 1.44 0 0 0 .328-1.026 1.366 1.366 0 0 0-2.729-.013 1.5 1.5 0 0 0 .06.553 1.4 1.4 0 0 0 .336.564l-1.603 5.027a.17.17 0 0 0 .016.14.16.16 0 0 0 .115.075 1.952 1.952 0 0 1 .385 3.782 1.84 1.84 0 0 1-2.097-.692.156.156 0 0 0-.217-.037l-.811.572a.16.16 0 0 0-.067.108.17.17 0 0 0 .028.124A3.15 3.15 0 0 0 26.097 30a3.1 3.1 0 0 0 1.871-.63 3.36 3.36 0 0 0 1.165-3.667 3.23 3.23 0 0 0-1.415-1.82Z'/><path fill='#e3f2fd' d='M31.845 17.545a3.15 3.15 0 0 0-5.177-1.459l-3.28-2.432a1.46 1.46 0 0 0-.193-.969 1.37 1.37 0 0 0-.857-.637 1.3 1.3 0 0 0-.308-.038h-.004a1.425 1.425 0 0 0-.003 2.847h.005a1.3 1.3 0 0 0 .631-.16l4.165 3.137a.16.16 0 0 0 .133.027.16.16 0 0 0 .104-.089 1.98 1.98 0 0 1 1.735-1.178h.001a1.933 1.933 0 0 1 1.897 1.963 1.96 1.96 0 0 1-1.296 1.863.166.166 0 0 0-.102.203l.28.98a.16.16 0 0 0 .079.098.15.15 0 0 0 .123.011 3.2 3.2 0 0 0 1.867-1.64 3.4 3.4 0 0 0 .2-2.527'/></svg>",
"folder-favicon-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-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='#fffde7' d='m24 24 6 4-2-6 4-4h-6l-1.999-6L22 18h-6l4 4-2 6z'/></svg>",
"folder-favicon": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' 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='#fffde7' d='m24 24 6 4-2-6 4-4h-6l-1.999-6L22 18h-6l4 4-2 6z'/></svg>",
"folder-features-open": "<svg fill='none' viewBox='0 0 32 32'><path fill='#689f38' d='M28.067 12H9.194a1.9 1.9 0 0 0-1.131.377c-.33.246-.576.593-.704.991L3.933 24V10h23.2c0-.53-.204-1.04-.567-1.414A1.9 1.9 0 0 0 25.199 8H14.686a1.9 1.9 0 0 1-1.237-.464l-1.245-1.072A1.9 1.9 0 0 0 10.966 6H3.933a1.9 1.9 0 0 0-1.367.586A2.04 2.04 0 0 0 2 8v16c0 .53.204 1.04.566 1.414.363.375.855.586 1.367.586H25.2l4.645-11.212a2.06 2.06 0 0 0-.163-1.889 1.96 1.96 0 0 0-.698-.66 1.9 1.9 0 0 0-.916-.239'/><path fill='#dcedc8' d='m27.587 13.584 2.132.509-6.03 6.03-2.7-2.7-1.456 1.454 4.156 4.155 6.485-6.485-.06.691L32 19.495l-1.885 2.259.262 2.988-2.79.666-1.46 2.583-2.627-1.184L20.873 28l-1.46-2.583-2.79-.667.262-2.996L15 19.495l1.885-2.265-.262-2.989 2.79-.657L20.873 11l2.627 1.185L26.127 11zM32 14.722l-1.826 1.825.203-2.297-.658-.157.827-.826z'/></svg>",
"folder-features": "<svg fill='none' viewBox='0 0 32 32'><path fill='#689f38' 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='#dcedc8' d='m27.587 13.583 2.132.51-6.03 6.03-2.7-2.701-1.456 1.455 4.156 4.155 6.485-6.486-.06.691L32 19.495l-1.885 2.259.262 2.988-2.79.666-1.46 2.584-2.627-1.185L20.873 28l-1.46-2.583-2.79-.667.262-2.996L15 19.495l1.885-2.265-.262-2.989 2.79-.658L20.873 11l2.627 1.185L26.127 11zM32 14.721l-1.826 1.825.203-2.296-.658-.157.827-.826z'/></svg>",
"folder-filter-open": "<svg viewBox='0 0 16 16'><path fill='#7e57c2' d='M14.483 6H4.721a1 1 0 0 0-.949.684L2 12V5h12a1 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'/><path fill='#d1c4e9' d='M15 6H7a.414.414 0 0 0-.293.707L10 10v4l1.293 1.293A.414.414 0 0 0 12 15v-5l3.293-3.293A.414.414 0 0 0 15 6'/></svg>",
"folder-filter": "<svg viewBox='0 0 16 16'><path fill='#7e57c2' 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='#d1c4e9' d='M15 6H7a.414.414 0 0 0-.293.707L10 10v4l1.293 1.293A.414.414 0 0 0 12 15v-5l3.293-3.293A.414.414 0 0 0 15 6'/></svg>",
"folder-firebase-open": "<svg viewBox='0 0 32 32'><path fill='#ff9100' 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='#ffe0b2' d='M24 28.014s-4.584.213-7-4.014c-.66-1.156-1.006-2.805-1-4 .012-2.264.962-3.881 1.038-4 .117-.181 2.954-.867 4.962 0s3.979 3.215 4 6-2.275 4.565-2.691 4.881.691 1.133.691 1.133M22 25.5c2.051-1.646 1.875-2.063 2-3.5s-1.007-3.071-2-4-2.934-.65-3.5-.5c-2.418 5.405 3.5 8 3.5 8'/><path fill='#ffe0b2' d='m24 28.014 2.527-.941s-1.988-1.265-2.909-2.168C21.381 22.71 20.021 20.085 20 16s4-8 4-8 8.063 6.276 8 12c-.644 8.183-8 8.014-8 8.014m4-3.023c1.044-1.135 1.95-2.042 2-4.991.075-4.381-6-9.5-6-9.5s-1.856 2.393-2 5.5c-.338 7.273 6 8.991 6 8.991'/><path fill='#ffe0b2' d='M22.34 25.64s3.453-.086 5.66-.649c3.451-.879-1.022 2.191-1.022 2.191L24 28.015l-1.313-.536z'/></svg>",
@ -459,6 +461,8 @@
"folder-less": "<svg viewBox='0 0 32 32'><path fill='#0277bd' 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='M20 21a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-5h2v-2h-2a2 2 0 0 0-2 2v4a1 1 0 0 1-1 1h-1v2h1a1 1 0 0 1 1 1v4a2 2 0 0 0 2 2h2v-2h-2Zm11-2a1 1 0 0 1-1-1v-4a2 2 0 0 0-2-2h-2v2h2v5a1 1 0 0 0 1 1 1 1 0 0 0-1 1v5h-2v2h2a2 2 0 0 0 2-2v-4a1 1 0 0 1 1-1h1v-2Z'/></svg>",
"folder-lib-open": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' 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='#f0f4c3' d='M23 16a3 3 0 0 0 .003-6H23a3 3 0 0 0-3 2.999V13a3 3 0 0 0 2.999 3zm0 3.973c-2.225-2.078-5.955-3.978-9-3.973v10c3.19 0 6.85 2.004 9 4 2.225-2.078 5.955-4.005 9-4V16c-3.045-.005-6.775 1.895-9 3.973'/></svg>",
"folder-lib": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' 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='#f0f4c3' d='M22.931 16a3 3 0 0 0 .003-6h-.003a3 3 0 0 0-3 2.999V13a3 3 0 0 0 2.999 3zm0 3.973c-2.225-2.078-5.955-3.978-9-3.973v10c3.19 0 6.85 2.004 9 4 2.226-2.078 5.955-4.005 9-4V16c-3.044-.005-6.774 1.895-9 3.973'/></svg>",
"folder-license-open": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='M14.48 6H4.72a1 1 0 0 0-.95.68L2 12V5h12a1 1 0 0 0-1-1H7.56a1 1 0 0 1-.64-.23l-.64-.54A1 1 0 0 0 5.64 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h11l2.4-5.6a1 1 0 0 0-.92-1.4'/><path fill='#ffccbc' d='M11 4.5a4.5 4.5 0 0 0-3 7.85V16l3-1.12L14 16v-3.65a4.5 4.5 0 0 0-3-7.85M11 6a3 3 0 1 1 0 6 3 3 0 0 1 0-6m0 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4'/></svg>",
"folder-license": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='m6.92 3.77-.64-.54A1 1 0 0 0 5.64 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.56a1 1 0 0 1-.64-.23'/><path fill='#ffccbc' d='M11 4.5a4.5 4.5 0 0 0-3 7.85V16l3-1.12L14 16v-3.65a4.5 4.5 0 0 0-3-7.85M11 6a3 3 0 1 1 0 6 3 3 0 0 1 0-6m0 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4'/></svg>",
"folder-link-open": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' 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='#d1c4e9' d='M736 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V576h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v190.547C595.489 821.653 512 768.467 512 704h64L448 576v128c0 106.039 128.942 192 288 192s288-85.961 288-192V576L896 704h64c0 64.467-83.489 117.653-192 126.547V640h32c17.673 0 32-14.327 32-32s-14.327-32-32-32h-32v-69.736c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
"folder-link": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' 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='#d1c4e9' d='M736 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V576h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v190.547C595.489 821.653 512 768.467 512 704h64L448 576v128c0 106.039 128.942 192 288 192s288-85.961 288-192V576L896 704h64c0 64.467-83.489 117.653-192 126.547V640h32c17.673 0 32-14.327 32-32s-14.327-32-32-32h-32v-69.736c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
"folder-linux-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='#ffecb3' d='M24.62 16.35c-.42.28-1.75 1.04-1.95 1.19a.825.825 0 0 1-1.14-.01c-.2-.16-1.53-.92-1.95-1.19-.48-.309-.45-.699.08-.919a6.16 6.16 0 0 1 4.91.03c.49.21.51.6.05.9Zm7.218 7.279A19.1 19.1 0 0 0 28 17.971a4.3 4.3 0 0 1-1.06-1.88c-.1-.33-.17-.67-.24-1.01a11.3 11.3 0 0 0-.7-2.609 4.06 4.06 0 0 0-3.839-2.47 4.2 4.2 0 0 0-3.95 2.4 6 6 0 0 0-.46 1.34c-.17.76-.32 1.55-.5 2.319a3.4 3.4 0 0 1-.959 1.71 19.5 19.5 0 0 0-3.88 5.348 6 6 0 0 0-.37.88c-.19.66.29 1.12.99.96.44-.09.88-.18 1.3-.31.41-.15.57-.05.67.35a6.73 6.73 0 0 0 4.24 4.498c4.119 1.56 8.928-.66 9.968-4.578.07-.27.17-.37.47-.27.46.14.93.24 1.4.35a.724.724 0 0 0 .92-.64 1.44 1.44 0 0 0-.16-.73Z'/></svg>",
@ -829,11 +833,13 @@
"latex.clone": "<svg viewBox='0 0 1024 1024'><path fill='#00bfa5' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
"latexmk": "<svg viewBox='0 0 1024 1024'><path fill='#00bfa5' d='M1024 128q-384 0-576 384L256 896l64-64 64-96c64 0 192-64 224-160-64 16-96-32-96-64 224 0 272-96 352-224 37.924-60.678 80-128 160-160M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128z'/></svg>",
"lbx": "<svg viewBox='0 0 1024 1024'><path fill='#2e7d32' d='M128 704v128c0 70.692 57.308 128 128 128h608c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M704 704v192h128V704z'/><path fill='#fff8e1' d='M192 704v96c0 53.184 42.816 96 96 96h544a96 96 0 0 1-96-96 96 96 0 0 1 96-96z'/><path fill='#ff1744' d='M320 832h192v192l-96-96-96 96z'/><path fill='#4caf50' d='M256 64c-70.692 0-128 57.308-128 128v640c0 11.088 1.557 21.787 4.207 32.047C146.767 807.565 197.672 768.07 256 768h608c17.728 0 32-14.272 32-32V96c0-17.728-14.272-32-32-32z'/><path fill='#e8f5e9' d='M448 128v64H256v64h256c0 33.778-26.676 86.11-73.947 144.959-7.257 9.034-15.068 18.276-23.123 27.611-4.479-5.242-8.838-10.461-13.002-15.634C370.55 373.95 352 336 352 320h-64c0 48 29.45 90.049 64.072 133.064 6.302 7.83 12.933 15.627 19.68 23.39-35.13 37.553-74.249 76.79-114.379 116.919l45.254 45.254c38.924-38.924 77.278-77.307 112.568-114.883 17.182 17.87 34.328 35.033 50.178 50.883l45.254-45.254c-16.799-16.799-34.744-34.801-52.309-53.17 10.301-11.813 20.31-23.56 29.63-35.162C538.675 377.889 576 318.222 576 256h128v-64H512v-64zm192 192L512 704h64l21.334-64h149.332L768 704h64L704 320zm32 96 53.334 160H618.666z'/></svg>",
"lean": "<svg viewBox='-2 -2 4.5 4.5'><path fill='#448aff' d='m-1.353-1.719-.366.188 1.97 3.75 1.968-3.75-.366-.188-.871 1.66H-.482zM-.313.25H.813L.25 1.375z' color='#000' style='-inkscape-stroke:none'/></svg>",
"lefthook": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M6 16v6H2zm5.106-6.537-3.317 1.775a2.22 2.22 0 0 0-.895 2.873l.333.71L14 11.571v-.193a2.006 2.006 0 0 0-2.894-1.915m18.82 7.545a2 2 0 0 0-.393-.744l-7.89-8.883a2.76 2.76 0 0 0-3.138-.384L16 8v4.559a3.97 3.97 0 0 1-1.566 3.18L16 20l8.457 2.204 4.624-2.979a2 2 0 0 0 .845-2.217'/><path fill='#b71c1c' fill-rule='evenodd' d='m2 22 4-2 4 2-4 2zm12.434-6.262a6 6 0 0 1-1.194.695l-2.544 1.136A6.55 6.55 0 0 1 8 18v.764l9.71 4.855a4.05 4.05 0 0 0 2.343.366 7.8 7.8 0 0 0 2.667-.82 24 24 0 0 0 1.737-.96zm-6.97-1.635 5.829-2.937a.5.5 0 0 1 .712.475c.007.417-.005.871-.005 1.153a2.1 2.1 0 0 1-1.367 2.03l-2.987 1.067c-1.629.581-3.103-1.324-2.182-1.788'/></svg>",
"lerna": "<svg viewBox='0 0 24 24'><path fill='#448aff' d='M7.57 8.21c-.41.08-.99.06-1.26.06-.4 0-1.27-.48-1.75-.98-.25-.26-.71-.82-1.01-1.25-.31-.43-.62-.78-.71-.78-.17 0-.28.6-.29 1.6 0 .12-.01.22-.01.31l.01.03-.14 8.47c.27.07.54.13.82.19.58.12 1.06.22 1.08.23.01.02-.19.34-.45.71-.95 1.35-1.14 2-.6 2 .17 0 .53-.07.82-.17.33-.1.93-.16 1.58-.16 1.34 0 2.68.33 4.37 1.08 1.61.71 2.14.88 4 1.25.85.18 1.59.37 1.64.41.05.05-.07.22-.28.38s-.38.33-.38.37.22.04.51-.01c.27-.04.89-.14 1.36-.19 1.43-.18 2.45-.82 2.47-1.56.04-.94-.25-3.03-.43-3.14-.02-.01-.38.11-.46.24-.07.13-.16.33-.25.56-.18.46-.15.56-1.57.66-1.03.05-2.02-.19-2.82-.65-.67-.39-1.66-.74-2.18-1.54-.42-.65-.86-1.72-.78-1.87.03-.04.17-.03.32.02.39.15.96.38 2.73.15 1.54-.2 3-.04 3.39.16s.92.47.94.99c.01.52.33.63.36.63.17-.03.59-1.42.83-1.83.2-.32.31-.5 1.1-1.04.62-.43 1.13-.83 1.11-.9-.01-.06-.36-.47-.77-.92-.65-.7-.83-.82-1.42-1.02-2.05-.67-4.23-2.12-5.36-3.54-1.25-1.58-1.6-1.94-2.5-2.53-1.01-.67-2.32-1.24-3.3-1.43-1-.19-.72.13-.29.8 1.53 1.75 1.49 1.81 1.49 2.97-.16.9-1 1.03-1.92 1.24m3.17 1.03c.17 0 .58.11 1.29.35 1.74.58 1.84.85 2.41 1.85.13.22.22.41.21.42s-.17.05-.35.09c-.45.09-1.16.09-1.58 0-.76-.16-.72-.19-1.11-.57-.16-.16-.35-.42-.41-.56-.04-.1-.03-.12.19-.3.05-.04.1-.11.1-.15 0-.07-.22-.37-.34-.47-.13-.11-.49-.54-.49-.59 0-.03.03-.05.1-.05z'/></svg>",
"less": "<svg viewBox='0 0 24 24'><path fill='#0277bd' d='M8 3a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2H3v2h1a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h2v-2H8v-5a2 2 0 0 0-2-2 2 2 0 0 0 2-2V5h2V3m6 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3z'/></svg>",
"liara": "<svg viewBox='0 0 16 16'><defs><linearGradient id='a' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15939 0 0 .16254 -.134 -.857)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient><linearGradient id='b' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15939 0 0 .16253 .195 -.856)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient><linearGradient id='c' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15784 0 0 .16493 .15 -.355)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient></defs><path fill='url(#a)' d='M8.5 8.811c0-.53.368-1.174.82-1.44l3.86-2.257c.452-.265.82-.052.82.479v4.596c0 .527-.368 1.17-.82 1.436l-3.86 2.261c-.452.265-.82.051-.82-.479zm0 0'/><path fill='url(#b)' d='M2 5.593c0-.53.368-.745.82-.479l3.86 2.258c.452.264.82.909.82 1.44v4.595c0 .53-.368.744-.82.48l-3.86-2.262c-.452-.264-.82-.909-.82-1.44zm0 0'/><path fill='url(#c)' d='M3.336 4.467c-.448-.27-.448-.706 0-.972l3.821-2.293c.447-.27 1.173-.27 1.62 0l3.888 2.332c.447.268.447.702 0 .971L8.843 6.799c-.447.268-1.173.268-1.624 0zm0 0'/></svg>",
"lib": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#8bc34a' 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>",
"license": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='M8 1a5.5 5.5 0 0 0-4 9.26V15l4-1.5 4 1.5v-4.74A5.49 5.49 0 0 0 8 1m0 1.5a4 4 0 1 1 0 8 4 4 0 0 1 0-8m0 2a2 2 0 1 0 0 4 2 2 0 0 0 0-4'/></svg>",
"lighthouse": "<svg viewBox='0 0 24 24'><path fill='#f4511e' d='M8.852 10.182V8.364h.851V4.727h-.851v-.909L12.258 2l3.407 1.818v.91h-.852v3.636h.852v1.818h-1.073L9.226 13.49l.477-3.31h-.851zm4.258-1.818V4.727h-1.703v3.637zM8 22l.034-.218L15.792 17l.443 3.073L13.11 22zm.894-6.21L15.077 12l.443 3.064-7.154 4.409z'/></svg>",
"lilypond": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='M10 8v11.023A4.986 4.986 0 1 0 11.9 24h.1V11.6l16-3.2v8.623A4.986 4.986 0 1 0 29.9 22h.1V4Z'/></svg>",
"lintstaged": "<svg viewBox='0 0 16 16'><path fill='#ff5252' d='M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7m0 2a5 5 0 0 1 5 5 5 5 0 0 1-.842 2.744L5.256 3.842A5 5 0 0 1 8 3M3.842 5.256l6.902 6.902A5 5 0 0 1 8 13a5 5 0 0 1-5-5 5 5 0 0 1 .842-2.744'/></svg>",
@ -846,6 +852,7 @@
"lottie": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M2 4v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2m20.237 8.11c-2.974.426-3.518 2.058-4.34 4.523-.92 2.764-2.145 6.436-7.635 7.217a1.996 1.996 0 1 1-.499-3.96c2.974-.426 3.518-2.058 4.34-4.523.92-2.764 2.145-6.436 7.635-7.217a1.996 1.996 0 1 1 .499 3.96'/></svg>",
"lua": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M30 6a3.86 3.86 0 0 1-1.167 2.833 4.024 4.024 0 0 1-5.666 0A3.86 3.86 0 0 1 22 6a3.86 3.86 0 0 1 1.167-2.833 4.024 4.024 0 0 1 5.666 0A3.86 3.86 0 0 1 30 6m-9.208 5.208A10.6 10.6 0 0 0 13 8a10.6 10.6 0 0 0-7.792 3.208A10.6 10.6 0 0 0 2 19a10.6 10.6 0 0 0 3.208 7.792A10.6 10.6 0 0 0 13 30a10.6 10.6 0 0 0 7.792-3.208A10.6 10.6 0 0 0 24 19a10.6 10.6 0 0 0-3.208-7.792m-1.959 7.625a4.024 4.024 0 0 1-5.666 0 4.024 4.024 0 0 1 0-5.666 4.024 4.024 0 0 1 5.666 0 4.024 4.024 0 0 1 0 5.666'/></svg>",
"luau": "<svg fill='none' viewBox='0 0 24 24'><path fill='#03a9f4' d='M22.495 6.331 6.33 2 2 18.164l16.164 4.33z'/><path fill='#fafafa' d='M19.933 7.81 16.7 6.944l-.866 3.233 3.233.866z'/></svg>",
"lynx": "<svg fill='none' viewBox='0 0 24 24'><path fill='#ff3d00' fill-rule='evenodd' d='m7.604 5.868-2.71 1.98a1.47 1.47 0 0 0-.57.905l-.266 1.352a.45.45 0 0 1-.09.194l-1.23 1.71c-.166.205-.162.748.292 1.095.174.161.406.522.67.932.56.868 1.264 1.958 1.858 1.845.84-.313 1.868-.413 2.658 0 1.534 1.405 1.158 2.701.617 4.56-.217.747-.46 1.585-.617 2.559.737-2.846 2.394-6.151 5.383-7.273-.538-.466-1.637-.9-2.61-1.004 0 0 2.988-2.714 6.673-3.96-2.572-6.493-6.819-9.596-6.819-9.596a.42.42 0 0 0-.743.159c-.073 1.026-.193 1.724-.39 2.382L8.215 1.88c-.156-.2-.468-.084-.467.172.253 1.492.218 2.328-.145 3.816m1.051-.136h.01a.4.4 0 0 0 .057-.009zm2.02-3.348c1.024 1.876 1.488 2.942 1.645 4.93-1.089-.656-1.563-.822-2.364-.818.468-1.507.61-2.414.72-4.112' clip-rule='evenodd'/><path fill='#ff3d00' d='M17.253 16.316C13.084 17.328 10.716 18.85 8.905 23c3.251-5.785 13.092-4.75 13.092-4.75-.183-.946-2.14-2.748-3.427-3.853 0 0 1.038-1.354 3.333-2.04 0 0-4.412.272-6.981 1.743.832.466 1.898 1.252 2.33 2.216'/></svg>",
"lyric": "<svg viewBox='0 0 32 32'><path fill='#50c5ef' d='M26 7.7c0-.1-.1-.2-.2-.3l-5.2-5.2c-.1-.1-.3-.2-.4-.2h-.1q-.15 0 0 0H7.7c-1 .1-1.7.8-1.7 1.8v24.5c0 1 .8 1.7 1.7 1.7h16.6c1 0 1.7-.8 1.7-1.7zq0 .15 0 0M22 12h-4v8c0 2.2-1.8 4-4 4s-4-1.8-4-4 1.8-4 4-4c.7 0 1.4.2 2 .6V8h6z'/></svg>",
"makefile": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m29.5 24.02-1.6-.92a4.4 4.4 0 0 0 .09-.9A1.3 1.3 0 0 0 28 22a5.6 5.6 0 0 0-.1-1.1l1.6-.92a.493.493 0 0 0 .18-.68l-1.5-2.6a.45.45 0 0 0-.18-.18V6.01a2.006 2.006 0 0 0-2-2H4a2.006 2.006 0 0 0-2 2V22a2.006 2.006 0 0 0 2 2h10.53l-.03.02a.493.493 0 0 0-.18.68l1.5 2.6a.493.493 0 0 0 .68.18l1.6-.92a5.9 5.9 0 0 0 1.9 1.09v1.85a.495.495 0 0 0 .5.5h3a.495.495 0 0 0 .5-.5v-1.85a5.9 5.9 0 0 0 1.9-1.09l1.6.92a.493.493 0 0 0 .68-.18l1.5-2.6a.493.493 0 0 0-.18-.68M24 22.01a1.99 1.99 0 0 1-.88 1.65l-.18.11a2.04 2.04 0 0 1-1.88 0l-.18-.11a1.99 1.99 0 0 1-.88-1.65V22a2 2 0 0 1 .88-1.66l.18-.11a2.04 2.04 0 0 1 1.88 0l.18.11A2 2 0 0 1 24 22Zm2-4.63-.1.06a5.9 5.9 0 0 0-1.9-1.09V14.5a.495.495 0 0 0-.5-.5h-3a.495.495 0 0 0-.5.5v1.85a5.9 5.9 0 0 0-1.9 1.09l-1.6-.92a.493.493 0 0 0-.68.18l-1.5 2.6a.493.493 0 0 0 .18.68l1.6.92A5.6 5.6 0 0 0 16 22v.01L4 22V10.01h22Z'/></svg>",
"markdoc-config": "<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'/><path fill='#ffb300' d='M12 12v18h18V12Zm10 14h-2v-6l-2 2-2-2v6h-2V16h2l2 2 2-2h2Zm6 2h-4V14h4Z'/></svg>",
@ -922,7 +929,7 @@
"opentofu": "<svg data-name='Layer 1' viewBox='0 0 32 32'><g stroke-width='.75'><path fill='#ffca28' d='M15.767 2.065a.44.44 0 0 1 .458 0l10.164 6.14c.33.197.33.716 0 .912l-10.164 6.14a.44.44 0 0 1-.458 0L5.603 9.118c-.33-.196-.33-.715 0-.912z'/><path fill='#fafafa' d='M27.299 10.556c.315-.189.701.063.701.456v12.273c0 .189-.093.37-.243.456l-10.264 6.195c-.315.189-.701-.063-.701-.456V17.207c0-.188.093-.37.243-.456z'/><path fill='#ffd600' d='M14.959 16.751 4.702 10.556c-.315-.189-.701.063-.701.456v12.273c0 .188.093.37.243.456l10.264 6.195c.315.189.701-.063.701-.456V17.207a.53.53 0 0 0-.243-.456zm-6.657 3.908-2.405-1.392v-.016c.058-.825.637-1.18 1.303-.794s1.16 1.36 1.102 2.186zm3.787 2.397-2.405-1.391v-.016c.057-.825.637-1.18 1.303-.794s1.16 1.36 1.102 2.186z'/></g></svg>",
"opentofu_light": "<svg data-name='Layer 1' viewBox='0 0 32 32'><g stroke-width='.657'><path fill='#263238' fill-rule='evenodd' d='M16.875 2.24a1.71 1.71 0 0 0-1.75 0L6.148 7.568s-.013.007-.02.014l-1.181.703C4.36 8.625 4 9.288 4 10.004V21.99c0 .717.36 1.379.941 1.727l9.064 5.381.038.02 1.081.643c.544.32 1.207.32 1.751 0l1.087-.642s.02-.014.032-.02l9.064-5.382c.581-.348.942-1.004.942-1.72V10.003c0-.716-.36-1.379-.942-1.72l-1.176-.697s-.019-.013-.025-.013zM17.45 16l8.4-4.985s.02-.014.026-.014l.1-.061c.279-.164.62.054.62.396v9.327c0 .342-.341.56-.62.397l-.075-.048s-.032-.02-.05-.028zM6.123 10.995s.013.007.02.013l8.4 4.985-8.395 4.991-.044.028-.082.048c-.278.163-.62-.055-.62-.397V11.33c0-.341.342-.56.62-.396l.107.061zm19.063-2.097-7.87-4.67c-.277-.164-.612.047-.619.382v10.113l8.49-5.04a.474.474 0 0 0 0-.778zm-18.38.779 8.488 5.039V4.61c-.006-.335-.348-.546-.62-.382l-7.869 4.67c-.278.178-.278.608 0 .779m.012 13.425a.468.468 0 0 1-.02-.779l8.502-5.046V27.39c-.006.328-.335.54-.607.396L6.818 23.11zm9.88 4.267V17.27l8.5 5.047c.266.184.26.614-.025.778l-7.869 4.678c-.272.15-.594-.062-.607-.39z'/><path fill='#ffca28' d='M15.795 3.559a.39.39 0 0 1 .405 0l8.975 5.333c.29.17.29.621 0 .792L16.2 15.017a.39.39 0 0 1-.405 0L6.82 9.684a.47.47 0 0 1 0-.792z'/><path fill='#ffd600' d='M5.397 11.329c0-.341.341-.56.62-.396l9.063 5.38a.46.46 0 0 1 .215.397v10.659c0 .341-.341.56-.62.396L5.619 22.39a.46.46 0 0 1-.215-.396V11.329z'/><path fill='#fafafa' d='M25.977 10.933c.278-.164.62.055.62.396v10.66c0 .163-.083.32-.215.395l-9.064 5.38c-.279.165-.62-.054-.62-.395v-10.66c0-.163.082-.32.215-.395z'/><path d='M9.202 19.694v.014l-2.124-1.209v-.014c.05-.717.563-1.024 1.15-.69.588.335 1.025 1.182.974 1.899m3.344 2.083v.014l-2.124-1.209v-.014c.05-.717.563-1.024 1.15-.69.588.335 1.025 1.182.974 1.899' class='cls-4'/></g></svg>",
"otne": "<svg viewBox='0 0 1024 1024'><g fill='#00c853'><path d='M512 254.16A257.84 257.84 0 0 0 254.16 512 257.84 257.84 0 0 0 512 769.841a257.84 257.84 0 0 0 257.841-257.84 257.84 257.84 0 0 0-257.84-257.842zM941.74 512A429.74 429.74 0 0 1 512 941.74 429.74 429.74 0 0 1 82.262 512 429.74 429.74 0 0 1 512 82.262 429.74 429.74 0 0 1 941.74 512'/><path d='M695.945 450.836h-92.08l-.005 122.318h92.084zm-122.854-122.78H450.92v367.89h122.17zm-152.942 122.78h-92.084v122.318h92.08z'/></g></svg>",
"oxlint": "<svg viewBox='0 0 16 16'><path fill='#ff7043' d='M8 1a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 1.75 2.38V7H5v1h2.25v5.5C5.467 13.193 3.897 12.523 3 11l1-1-3-2v2c0 2.266 2.88 5 7 5 2.764.13 7-1.779 7-5V8l-3 2 1 1c-.755 1.631-2.591 2.091-4.25 2.5V8H11V7H8.75V5.88A2.5 2.5 0 0 0 10.5 3.5 2.5 2.5 0 0 0 8 1m0 1.25A1.25 1.25 0 0 1 9.25 3.5 1.25 1.25 0 0 1 8 4.75 1.25 1.25 0 0 1 6.75 3.5 1.25 1.25 0 0 1 8 2.25'/></svg>",
"oxc": "<svg viewBox='0 0 16 16'><path fill='#ff7043' d='M8 1a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 1.75 2.38V7H5v1h2.25v5.5C5.467 13.193 3.897 12.523 3 11l1-1-3-2v2c0 2.266 2.88 5 7 5 2.764.13 7-1.779 7-5V8l-3 2 1 1c-.755 1.631-2.591 2.091-4.25 2.5V8H11V7H8.75V5.88A2.5 2.5 0 0 0 10.5 3.5 2.5 2.5 0 0 0 8 1m0 1.25A1.25 1.25 0 0 1 9.25 3.5 1.25 1.25 0 0 1 8 4.75 1.25 1.25 0 0 1 6.75 3.5 1.25 1.25 0 0 1 8 2.25'/></svg>",
"packship": "<svg viewBox='0 0 16 16'><path fill='#9575cd' d='M2.997 14.967c-.344-.086-.654-.345-.765-.64-.136-.36-.157-.283 1.119-4.186s1.181-3.58 1.01-3.467c-.741.491-1.703.082-1.95-.83-.175-.648.027-1.006.899-1.587 1.274-.85 1.979-1.476 2.603-2.312.492-.659.682-.798 1.194-.873.623-.09 1.204.138 1.418.56.075.147.02.153.417-.053 4.991-2.592 6.93 4.168 2.114 7.369-1.361.904-3.966 1.598-4.84 1.29l-.128-.045-.144.439c-.08.241-.378 1.16-.663 2.042-.622 1.929-.645 1.972-1.152 2.21-.228.106-.854.153-1.132.083m.729-.772c.272-.068.224.052 1.27-3.18 1.267-3.918 1.463-4.385 2.38-5.681 2.03-2.868 4.835-3.397 4.577-.864-.218 2.145-2.177 3.72-5 4.02-.493.052-.577.117-.647.496-.1.542.18.631 1.35.431 3.043-.52 5.106-2.398 5.391-4.906.365-3.207-3.167-3.65-5.56-.698-.289.356-.294.418.059-.698.392-1.241.389-1.266-.198-1.266-.306 0-.397.047-.604.31-.072.09-.243.31-.38.486-.58.745-1.443 1.49-2.632 2.275-.652.43-.708.542-.468.922.157.247.337.32.56.226.238-.099 1.576-1.11 2.002-1.51.124-.118.226-.205.226-.193s-.7 2.17-1.555 4.795a462 462 0 0 0-1.556 4.82c0 .198.407.31.785.215m3.962-6.61c2.008-.431 3.407-1.742 3.486-3.266.031-.597-.03-.691-.436-.666-1.12.068-3.05 2.1-3.756 3.956l-.034.087.218-.022a6 6 0 0 0 .522-.088'/></svg>",
"palette": "<svg viewBox='0 0 16 16'><path fill='#4fc3f7' d='M12.278 8a1.167 1.167 0 0 1-1.167-1.167 1.167 1.167 0 0 1 1.167-1.166 1.167 1.167 0 0 1 1.166 1.166A1.167 1.167 0 0 1 12.278 8M9.944 4.889a1.167 1.167 0 0 1-1.166-1.167 1.167 1.167 0 0 1 1.166-1.166 1.167 1.167 0 0 1 1.167 1.166A1.167 1.167 0 0 1 9.944 4.89m-3.888 0a1.167 1.167 0 0 1-1.167-1.167 1.167 1.167 0 0 1 1.167-1.166 1.167 1.167 0 0 1 1.166 1.166A1.167 1.167 0 0 1 6.056 4.89M3.722 8a1.167 1.167 0 0 1-1.166-1.167 1.167 1.167 0 0 1 1.166-1.166A1.167 1.167 0 0 1 4.89 6.833 1.167 1.167 0 0 1 3.722 8M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 1.167 1.167 0 0 0 1.167-1.167c0-.303-.117-.575-.304-.777a1.2 1.2 0 0 1-.295-.778 1.167 1.167 0 0 1 1.166-1.167h1.377A3.89 3.89 0 0 0 15 7.222C15 3.784 11.866 1 8 1'/></svg>",
"panda": "<svg viewBox='0 0 24 24'><path fill='#ffd740' d='M4.524 20.862c-.258-.317-.958-2.683-1.319-4.451-1.238-6.075.1-10.397 3.824-12.354 1.596-.838 2.918-1.114 5.37-1.118 3.212-.007 5.102.617 6.808 2.244 2.52 2.403 2.735 6.732.459 9.222-1.267 1.387-4.598 2.82-6.551 2.82h-.593l-.408-1.239c-.224-.68-.456-1.502-.516-1.825l-.108-.586.656.088c.777.104 1.89-.27 2.365-.798.998-1.102.824-3.595-.302-4.333-1.063-.697-3.124-.653-4.166.089-1.888 1.345-1.382 6.248 1.172 11.343.248.495.406.944.351.999-.054.055-1.624.1-3.49.1-2.519 0-3.431-.052-3.552-.2z'/></svg>",
@ -971,7 +978,7 @@
"python": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='M9.86 2A2.86 2.86 0 0 0 7 4.86v1.68h4.29c.39 0 .71.57.71.96H4.86A2.86 2.86 0 0 0 2 10.36v3.781a2.86 2.86 0 0 0 2.86 2.86h1.18v-2.68a2.85 2.85 0 0 1 2.85-2.86h5.25c1.58 0 2.86-1.271 2.86-2.851V4.86A2.86 2.86 0 0 0 14.14 2zm-.72 1.61c.4 0 .72.12.72.71s-.32.891-.72.891c-.39 0-.71-.3-.71-.89s.32-.711.71-.711'/><path fill='#fdd835' d='M17.959 7v2.68a2.85 2.85 0 0 1-2.85 2.859H9.86A2.85 2.85 0 0 0 7 15.389v3.75a2.86 2.86 0 0 0 2.86 2.86h4.28A2.86 2.86 0 0 0 17 19.14v-1.68h-4.291c-.39 0-.709-.57-.709-.96h7.14A2.86 2.86 0 0 0 22 13.64V9.86A2.86 2.86 0 0 0 19.14 7zM8.32 11.513l-.004.004.038-.004zm6.54 7.276c.39 0 .71.3.71.89a.71.71 0 0 1-.71.71c-.4 0-.72-.12-.72-.71s.32-.89.72-.89'/></svg>",
"pytorch": "<svg viewBox='0 0 32 32'><circle cx='20' cy='8' r='2' fill='#f4511e'/><path fill='#f4511e' d='M25.573 11.335a11.4 11.4 0 0 0-1.1-1.219l-2.825 2.832a9 9 0 0 1 .746.812 7.36 7.36 0 0 1 1.443 3.011 8 8 0 0 1 .164 1.23A8 8 0 0 1 8 18a5.76 5.76 0 0 1 .695-2.762 7.4 7.4 0 0 1 1.277-1.896c.18-.18 1.814-1.746 3.74-3.584L16 7.553V2l-5.057 4.873c-1.112 1.06-3.713 3.545-3.877 3.722a11.5 11.5 0 0 0-1.99 2.942A9.8 9.8 0 0 0 4 18a12 12 0 1 0 24 0 12.6 12.6 0 0 0-.254-2.074 11.26 11.26 0 0 0-2.173-4.591'/></svg>",
"qsharp": "<svg viewBox='0 0 24 24'><path fill='#fbc02d' d='M11.938 16.414c.9-1.101 1.468-2.936 1.468-4.735 0-1.963-.697-3.854-1.872-5.12-1.156-1.248-2.66-1.853-4.57-1.853s-3.413.605-4.569 1.853C1.22 7.825.523 9.716.523 11.716s.697 3.89 1.872 5.157c1.156 1.248 2.68 1.853 4.57 1.853 1.376 0 2.404-.275 3.468-.917l1.578 1.486 1.395-1.486zm-3.395-3.212-1.396 1.487 1.413 1.34c-.422.22-1.027.348-1.615.348-2.202 0-3.67-1.853-3.67-4.66 0-2.809 1.468-4.662 3.689-4.662 2.239 0 3.689 1.835 3.689 4.68 0 1.1-.202 2.092-.606 2.9z' aria-label='Q'/><path fill='#fbc02d' d='m17.589 4.728-.611 4h-1.5l-.34 2h1.5l-.32 2h-1.5l-.34 2h1.5l-.61 4h2l.61-4h1l-.61 4h2l.61-4h1.5l.34-2h-1.5l.32-2h1.5l.34-2h-1.5l.611-4h-2l-.611 4h-1l.611-4zm1.049 6h1l-.32 2h-1z'/></svg>",
"quarto": "<svg viewBox='0 0 4.233 4.233'><path fill='#82b1ff' d='M2.381.26v1.588h1.588C3.904 1.076 3.153.325 2.381.26m-.53 0C1.082.326.331 1.078.264 1.848h1.588zM.264 2.377c.066.77.818 1.521 1.588 1.587V2.377zm2.117 0v1.587c.77-.066 1.521-.818 1.588-1.587z' paint-order='stroke fill markers'/></svg>",
"quarto": "<svg viewBox='0 0 16 16'><path fill='#82b1ff' d='M9 1v6h6c-.246-2.918-3.083-5.754-6-6M7 1c-2.906.25-5.747 3.09-6 6h6zM1 9c.25 2.91 3.09 5.75 6 6V9zm8 0v6c2.91-.25 5.747-3.093 6-6z' paint-order='stroke fill markers'/></svg>",
"quasar": "<svg viewBox='0 0 50.843 50.843'><path fill='#1976d2' d='M29.585 25.422a4.16 4.16 0 0 1-4.16 4.159 4.16 4.16 0 0 1-4.159-4.16 4.16 4.16 0 0 1 4.16-4.159 4.16 4.16 0 0 1 4.159 4.16M43.888 14.76a21.3 21.3 0 0 0-3.267-4.272l-4.808 2.776a16 16 0 0 0-5.02-2.91c-1.642 1.664-2.946 3.523-3.886 5.545 5.352-.364 10.88 1.573 16.012 5.582l3.026-1.747a21.3 21.3 0 0 0-2.057-4.974m.001 21.32a21.3 21.3 0 0 0 2.066-4.966l-4.808-2.776c.359-1.938.356-3.904.01-5.803-2.262-.59-4.524-.79-6.745-.593 2.992 4.454 4.078 10.21 3.172 16.659l3.026 1.747a21.3 21.3 0 0 0 3.28-4.269zM25.427 46.74a21.3 21.3 0 0 0 5.333-.694v-5.552a16 16 0 0 0 5.03-2.893c-.62-2.253-1.578-4.312-2.859-6.137-2.36 4.817-6.802 8.636-12.84 11.076v3.494a21.3 21.3 0 0 0 5.336.706M6.963 36.082a21.3 21.3 0 0 0 3.267 4.272l4.809-2.777a16 16 0 0 0 5.02 2.91c1.642-1.664 2.946-3.523 3.886-5.544-5.353.364-10.88-1.573-16.012-5.583l-3.027 1.747a21.3 21.3 0 0 0 2.057 4.975m-.001-21.32a21.3 21.3 0 0 0-2.066 4.966l4.809 2.776a16 16 0 0 0-.01 5.802c2.262.59 4.524.79 6.744.593-2.991-4.453-4.078-10.209-3.171-16.658l-3.026-1.747a21.3 21.3 0 0 0-3.28 4.269zm18.462-10.66a21.3 21.3 0 0 0-5.333.694v5.552a16 16 0 0 0-5.03 2.893c.62 2.253 1.578 4.312 2.86 6.137 2.36-4.818 6.802-8.636 12.84-11.076V4.808a21.3 21.3 0 0 0-5.337-.706'/></svg>",
"quokka": "<svg viewBox='0 0 16 16'><path fill='#ff6d00' d='M8 2v6H2v6h12V2z' paint-order='fill markers stroke'/></svg>",
"qwik": "<svg viewBox='0 0 24 24'><path fill='#29b6f6' d='m19.26 22.182-3.657-3.636-.056.008v-.04l-7.776-7.678 1.916-1.85-1.126-6.46-5.341 6.62c-.91.917-1.078 2.408-.423 3.508l3.337 5.534a2.8 2.8 0 0 0 2.435 1.356l1.653-.016z'/><path fill='#b388ff' d='m21.255 9.018-.734-1.356-.383-.693-.152-.272-.016.016-2.012-3.484a2.82 2.82 0 0 0-2.467-1.411l-1.765.047-5.261.016A2.82 2.82 0 0 0 6.054 3.27L2.852 9.616 8.577 2.51l7.505 8.245-1.334 1.347.799 6.451.008-.016v.016h-.016l.016.016.623.606 3.025 2.958c.128.12.336-.024.248-.175l-1.868-3.676 3.257-6.02.104-.12c.04-.048.08-.096.112-.143.638-.87.726-2.034.2-2.983z'/><path fill='#eceff1' d='M16.106 10.724 8.576 2.52l1.07 6.427-1.916 1.858 7.8 7.742-.702-6.426z'/></svg>",
@ -1126,6 +1133,7 @@
"uml": "<svg viewBox='0 0 100 100'><path fill='#b39ddb' d='M87 76.652 53.84 93.907l-.038-41.04 13.9-7.15v29.622l19.224-9.85z'/><path fill='#fbc02d' d='m38.693 89.604 8.576 4.303V52.743l-13.027-6.29-4.126 19.643-4.16-23.69L13 36.077V77.28l8.54 4.378V56.826l4.669 26.817 7.599 3.863 4.885-22.293z'/><path fill='#f06292' d='m45.237 6.093-9.775 8.755s19.072 9.931 21.39 11.105c2.317 1.173 5.615 3.43 2.05 6.771s-7.487 2.89-10.16 1.535a21830 21830 0 0 1-22.458-11.466l-10.07 8.667S35.642 41.48 38.85 43.196c3.208 1.715 15.15 5.958 26.47-2.98 11.318-8.937 9.714-12.188 9.714-12.82s-.267-3.972-2.228-6.048c-1.96-2.077-7.664-5.056-10.07-6.32S45.239 6.092 45.239 6.092z'/></svg>",
"uml_light": "<svg viewBox='0 0 100 100'><path fill='#9575cd' d='M67.702 45.716V75.34l19.224-9.85L87 76.653 53.84 93.907l-.038-41.04z'/><path fill='#f9a825' d='m30.116 66.096-4.16-23.69L13 36.077V77.28l8.54 4.378V56.826l4.669 26.817 7.599 3.863 4.885-22.293v24.391l8.576 4.303V52.743l-13.027-6.29z'/><path fill='#ec407a' d='m45.237 6.093-9.775 8.755s19.072 9.931 21.39 11.105c2.317 1.174 5.615 3.43 2.05 6.772-3.565 3.34-7.487 2.889-10.16 1.535a21830 21830 0 0 1-22.458-11.468l-10.07 8.667S35.641 41.482 38.85 43.196c3.208 1.716 15.15 5.959 26.47-2.979 11.318-8.938 9.714-12.188 9.714-12.82s-.267-3.972-2.228-6.049c-1.96-2.076-7.664-5.056-10.07-6.32S45.239 6.093 45.239 6.093z'/></svg>",
"unity": "<svg viewBox='0 0 16 16'><path fill='#42a5f5' d='M8 6.5 5 5l2-1V2L2 5v5l2-1V6.5L7 8v4.5L4 11l-2 1 6 3 6-3-2-1-3 1.5V8l3-1.5V9l2 1V5L9 2v2l2 1Z'/></svg>",
"unlicense": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='m2.5 1.5-1 1 1.57 1.57A5.5 5.5 0 0 0 4 10.26V15l4-1.5 4 1.5v-2l1.5 1.5 1-1zM8 1c-1.14 0-2.19.34-3.07.93l1.1 1.1a4 4 0 0 1 5.45 5.45l1.08 1.08A5.48 5.48 0 0 0 8 1m0 3.5q-.23 0-.45.05l2.4 2.4A2 2 0 0 0 8 4.5m-3.79.71L9.3 10.3a3.99 3.99 0 0 1-5.1-5.1'/></svg>",
"unocss": "<svg viewBox='0 0 32 32'><circle cx='24' cy='24' r='6' fill='#78909c'/><path fill='#546e7a' d='M2 18v6a6 6 0 0 0 12 0v-6Z'/><path fill='#b0bec5' d='M30 14V8a6 6 0 0 0-12 0v6Z'/></svg>",
"url": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M10 14h12v4H10z'/><path fill='#42a5f5' d='M12 22H9.562A5.57 5.57 0 0 1 4 16.438v-.876A5.57 5.57 0 0 1 9.562 10H12V6H9.562A9.56 9.56 0 0 0 0 15.562v.876A9.56 9.56 0 0 0 9.562 26H12ZM22.438 6H20v4h2.438A5.57 5.57 0 0 1 28 15.562v.876A5.57 5.57 0 0 1 22.438 22H20v4h2.438A9.56 9.56 0 0 0 32 16.438v-.876A9.56 9.56 0 0 0 22.438 6'/></svg>",
"uv": "<svg viewBox='0 0 16 16'><path fill='#e040fb' d='M2 2v11c0 .5.5 1 1 1h8c.5 0 1-.5 1-1h1v1h1V2H8v8H7V2z'/></svg>",
@ -1159,6 +1167,8 @@
"wakatime_light": "<svg fill='none' viewBox='0 0 340 340'><path stroke='#455a64' stroke-width='33.39' d='M170 44.788c-69.154 0-125.212 56.058-125.212 125.212s56.058 125.213 125.213 125.213S295.212 239.155 295.212 170 239.155 44.788 170 44.788z'/><path fill='#455a64' d='M186.846 206.343c-1.205 1.588-3.011 2.61-5.035 2.61a6 6 0 0 1-.591-.034 7 7 0 0 1-.7-.109 6.7 6.7 0 0 1-1.15-.385 8 8 0 0 1-.547-.28 6.6 6.6 0 0 1-.856-.591 7 7 0 0 1-.42-.367 8 8 0 0 1-.586-.64 7.5 7.5 0 0 1-.754-1.144l-7.378-11.854-7.374 11.854c-1.157 2.107-3.249 3.55-5.652 3.55-2.412 0-4.514-1.454-5.636-3.607l-32.252-46.985c-1.06-1.278-1.712-2.973-1.712-4.844 0-3.96 2.911-7.173 6.501-7.173 2.324 0 4.358 1.35 5.508 3.375l27.224 40.228 7.663-12.477c1.104-2.224 3.248-3.734 5.71-3.734 2.252 0 4.238 1.266 5.404 3.188l7.903 12.972 42.712-61.15c1.16-1.967 3.164-3.269 5.45-3.269 3.59 0 6.5 3.212 6.5 7.172 0 1.73-.553 3.317-1.478 4.555z'/></svg>",
"wallaby": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M16 2v14H2v14h28V2z'/></svg>",
"wally": "<svg viewBox='0 0 32 32'><path fill='#e65100' d='M8.454 3.084c-.897-.112-1.438.473-2.502 2.43-1.457 2.682-3.888 1.135-1.765 5.275a4.7 4.7 0 0 0 .759 1.122l5.749-9.219c-.84-.614-1.513.448-2.241.392'/><path fill='#ffcc80' d='m28.565 29.985-5.982-8.28a1.67 1.67 0 0 0-.57-1.362c-1.794-1.789-2.69-3.51-1.905-6.975a3.94 3.94 0 0 0-1.326-3.766 29 29 0 0 0 .206-3.22s-.673-2.907-2.354-3.13c0 0-4.426-2.236-7.956.279S7.95 5.71 5.82 8.897a4.5 4.5 0 0 0-.444.867 7 7 0 0 0-1.237.307c-1.4.56-1.344 2.07.56 3.635a1 1 0 0 0 .29.148 8.2 8.2 0 0 1-.29 2.198c-.595 1.674.853 3.91 3.024 1.789a7 7 0 0 0 1.648.942 8.1 8.1 0 0 1 .707 2.522l-.558 8.68Z'/><path fill='#3e2723' d='M8.651 9.017a.803.803 0 0 1-.807-.805v-1.88a.807.807 0 0 1 1.615 0v1.88a.803.803 0 0 1-.808.805m5.314 0a.814.814 0 0 1-.813-.811V6.339a.814.814 0 0 1 1.627 0v1.948a.8.8 0 0 1-.814.73'/><path fill='#e65100' d='M17.082 14.15a6.06 6.06 0 0 1-4.818 4.528 4.75 4.75 0 0 1-5.828-3.13 3.8 3.8 0 0 0 2.999 3.679c.84 1.302.355 3.547.307 4.874a5.4 5.4 0 0 1-.061.84l-1.08 5.044 5.421.015c-.724-.397-1.02-2.476-1.14-3.901l-.079-1.405a1.415 1.415 0 0 1 .974-1.432c2.796-1.81-.672-1.789-.672-2.292a3.6 3.6 0 0 1 1.068-2.195c2.535-1.273 3.114-4.013 2.91-4.624'/><path fill='#e65100' d='M20.518 17.614c.588-.523 1.012-2.457 1.492-3.362.785-1.48 4.797-2.989 3.136-5.446-1.55-2.292-2.24-1.658-3.136-3.28-.84-1.452-1.073-1.763-3.154-1.763a7.7 7.7 0 0 1-2.296-1.124A3.07 3.07 0 0 0 14.531 2c-3.98 0-4.9-.091-5.853 1.195 0 0 4.429-1.841 8.025.533 1.213 1.634 2.441 4.61 1.612 9.64a8.8 8.8 0 0 0 .093 3.228 1.5 1.5 0 0 0 .096.466s4.621 9.882 9.222 12.922H29s-5.867-7.368-8.482-12.371M5.035 16.05a2.496 2.496 0 0 0-1.176-3.24 3 3 0 0 1 .56 3.185c-.56 1.118 0 3.242 2.073 2.627 0 0-2.41.112-1.457-2.571'/><path fill='#b71c1c' d='M9.736 23.933a9.63 9.63 0 0 0 8.41-.28c3.195-1.733 3.788-3.732 3.451-4.123a2.42 2.42 0 0 1 2.053 1.036c.56.839-.46 3.645-5.167 5.602-4.427 1.788-7.848.974-8.632.191-.729-.782-.115-2.426-.115-2.426m-6.55-13.192c-.616.615.393 2.46 1.625 3.018 1.401.615 3.418-1.788 3.026-2.459-.448-.894-3.362-1.844-4.65-.559'/></svg>",
"warp": "<svg viewBox='0 0 16 16'><g fill='#cfd8dc'><path d='M8.443 2h5.064C14.362 2 15 2.716 15 3.6v6.8c0 .884-.718 1.6-1.573 1.6H6.224z'/><path d='M7 4H2.533C1.686 4 1 4.715 1 5.599v6.802C1 13.284 1.717 14 2.563 14H7.79L8 13H5z'/></g></svg>",
"warp_light": "<svg viewBox='0 0 16 16'><g fill='#546e7a'><path d='M8.443 2h5.064C14.362 2 15 2.716 15 3.6v6.8c0 .884-.718 1.6-1.573 1.6H6.224z'/><path d='M7 4H2.533C1.686 4 1 4.715 1 5.599v6.802C1 13.284 1.717 14 2.563 14H7.79L8 13H5z'/></g></svg>",
"watchman": "<svg viewBox='0 0 420 419'><circle cx='210' cy='209.5' r='188.15' fill='#fafafa' paint-order='stroke fill markers'/><path fill='#304ffe' d='M191.07 397.1c-35.512-4.049-66.779-16.485-95.318-37.913-22.723-17.061-44.027-43.274-56.077-68.997l-3.932-8.393 25.692-26.37c14.13-14.505 27.522-28.059 29.758-30.12l4.067-3.748 12.5 11.364c16.495 14.996 26.818 23.219 41.05 32.697 14.94 9.95 23.867 14.578 35.877 18.597 22.823 7.637 42.099 5.991 66.082-5.642 17.83-8.65 44.399-28.24 66.179-48.797l8.878-8.38 29.147 29.137c16.03 16.025 29.147 29.84 29.147 30.7s-1.6 4.912-3.554 9.005c-18.398 38.533-49.46 69.834-87.797 88.466-17.732 8.619-33.936 13.787-53.563 17.084-10.16 1.708-38.005 2.465-48.137 1.31zm6.3-123.69c-17.457-3.809-39.276-16.397-63.835-36.829-13.001-10.816-26.615-23.771-26.615-25.327 0-1.265 16.792-17.101 29.7-28.009 20.328-17.179 36.936-27.484 53.753-33.355 6.275-2.19 8.25-2.443 19.147-2.443 10.892 0 12.873.253 19.136 2.44 22.614 7.894 50.68 27.157 79.189 54.35 3.836 3.659 6.975 7.021 6.975 7.472 0 1.1-21.726 20.758-32.4 29.316-19.403 15.557-41.794 28.276-56.169 31.907-8.267 2.088-20.566 2.29-28.881.477zm37.472-20.429c14.201-7.184 18.451-9.747 19.779-11.925 1.556-2.552 1.692-4.934 1.692-29.774 0-24.91-.132-27.216-1.705-29.795-1.34-2.2-5.607-4.783-20.022-12.124-18.175-9.256-18.368-9.329-24.812-9.355-6.44-.026-6.632.044-22.95 8.376-10.12 5.168-17.582 9.58-19.38 11.462l-2.925 3.06.003 27.237c.003 25.164.134 27.452 1.712 30.04 1.301 2.134 5.186 4.602 16.288 10.35 20.15 10.433 22.925 11.538 29.04 11.571 4.82.026 6.487-.627 23.28-9.123m-40.872-12.388c-7.315-3.808-13.914-7.538-14.665-8.29-1.181-1.181-1.28-4.249-.736-22.667l.63-21.3 13.888-7.202c8.096-4.199 14.986-7.202 16.52-7.202 2.953 0 28.618 12.505 31.424 15.311 1.68 1.682 1.788 3.033 1.788 22.477v20.69l-3.46 2.183c-1.902 1.202-8.376 4.65-14.386 7.662-8.03 4.025-11.825 5.448-14.315 5.37-2.329-.075-7.546-2.273-16.688-7.032M29.5 263.432c-1.475-3.866-4.192-15.727-5.967-26.05-2.405-13.986-2.19-43.337.424-58.05 2.41-13.562 4.216-20.627 5.613-21.959.772-.736 7.977 6.088 27.938 26.459 14.794 15.098 26.902 27.655 26.906 27.906s-12.122 12.581-26.948 27.4L30.51 266.082zm333.59-24.851c-14.17-14.479-25.763-26.679-25.763-27.11 0-1.574 51.776-53.76 53.1-53.521 1.493.27 3.338 6.773 5.975 21.06 2.353 12.751 2.343 48.232-.017 61.072-2.37 12.886-5.358 24.1-6.527 24.49-.553.184-12.599-11.512-26.768-25.991M64.75 170.903c-26.893-26.902-29.95-30.252-29.354-32.175 1.335-4.308 8.33-18.529 12.565-25.546 28.808-47.732 75.832-79.809 131.42-89.647 15.231-2.696 45.201-2.944 59.361-.492 41.827 7.244 76.236 24.833 104.91 53.625 15.582 15.647 27.713 32.758 36.971 52.151 6.183 12.95 8.732 8.549-24.429 42.185-15.984 16.213-29.386 29.61-29.782 29.768-.396.16-5.695-4.244-11.775-9.786-32.548-29.67-61.86-48.734-85.116-55.361-7.771-2.215-9.327-2.357-22.05-2.009-12.468.34-14.427.627-21.965 3.212-23.775 8.152-49.675 26.108-80.457 55.78-4.75 4.579-9.006 8.325-9.457 8.325s-14.329-13.513-30.839-30.029z'/></svg>",
"webassembly": "<svg viewBox='0 0 32 32'><path fill='#7c4dff' d='M22 18h4v4h-4z'/><path fill='#7c4dff' d='M20 2a4 4 0 0 1-8 0H2v28h28V2Zm-2 24h-2v2h-4v-2h-2v2H6v-2H4V16h2v10h4V16h2v10h4V16h2Zm10 2h-2v-4h-4v4h-2V18h2v-2h4v2h2Z'/></svg>",
"webhint": "<svg viewBox='0 0 120 120'><path fill='#1e88e5' d='M115.45 23.033S97.961 33.27 97.534 33.412c-.427.284-.852.57-1.137.854-1.422 1.421-1.848 3.41-1.422 5.26.285.852.711 1.849 1.422 2.56.711.71 1.564 1.137 2.559 1.422 1.848.426 3.84 0 5.262-1.422q.638-.64.851-1.28l.143-.427 2.56-4.692zm-39.102 9.242c-27.441 0-31.99 13.08-31.99 29.29 0 3.838.569 7.962-1.99 11.942-3.84 5.972-8.957 5.828-10.236 5.828-1.706 0-7.962-.993-8.246-2.841h.994c6.682 0 11.658-5.404 11.658-12.655v-2.56h-5.686c-4.123 0-7.82 1.849-10.238 5.12-2.417-3.271-6.113-5.12-10.236-5.12h-5.83v2.56c0 7.11 5.688 12.795 12.797 12.795h1.848c0 4.124 5.687 20.332 47.63 20.332 16.352 0 40.665-2.843 40.665-33.697 0-5.829-1.848-11.23-4.691-15.78-.996.284-1.992.568-3.13.568a8.92 8.92 0 0 1-8.956-8.957q0-1.493.425-2.986c-4.265-2.702-8.53-3.838-14.787-3.838z'/></svg>",

View File

@ -1354,8 +1354,11 @@ editor.this_file_locked = File is locked
editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file.
editor.fork_before_edit = You must fork this repository to make or propose changes to this file.
editor.delete_this_file = Delete File
editor.delete_this_directory = Delete Directory
editor.must_have_write_access = You must have write access to make or propose changes to this file.
editor.file_delete_success = File "%s" has been deleted.
editor.directory_delete_success = Directory "%s" has been deleted.
editor.delete_directory = Delete directory '%s'
editor.name_your_file = Name your file…
editor.filename_help = Add a directory by typing its name followed by a slash ('/'). Remove a directory by typing backspace at the beginning of the input field.
editor.or = or
@ -1482,6 +1485,7 @@ projects.column.new_submit = "Create Column"
projects.column.new = "New Column"
projects.column.set_default = "Set Default"
projects.column.set_default_desc = "Set this column as default for uncategorized issues and pulls"
projects.column.default_column_hint = "New issues added to this project will be added to this column"
projects.column.delete = "Delete Column"
projects.column.deletion_desc = "Deleting a project column moves all related issues to the default column. Continue?"
projects.column.color = "Color"
@ -3038,7 +3042,7 @@ dashboard.update_migration_poster_id = Update migration poster IDs
dashboard.git_gc_repos = Garbage-collect all repositories
dashboard.resync_all_sshkeys = Update the '.ssh/authorized_keys' file with Gitea SSH keys
dashboard.resync_all_sshprincipals = Update the '.ssh/authorized_principals' file with Gitea SSH principals
dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories
dashboard.resync_all_hooks = Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)
dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist
dashboard.sync_external_users = Synchronize external user data
dashboard.cleanup_hook_task_table = Clean up hook_task table

View File

@ -3038,7 +3038,6 @@ dashboard.update_migration_poster_id=Actualiser les ID des affiches de migration
dashboard.git_gc_repos=Exécuter le ramasse-miette des dépôts
dashboard.resync_all_sshkeys=Mettre à jour le fichier « ssh/authorized_keys » avec les clés SSH Gitea.
dashboard.resync_all_sshprincipals=Mettre à jour le fichier « .ssh/authorized_principals » avec les principaux de Gitea SSH.
dashboard.resync_all_hooks=Re-synchroniser les déclencheurs Git pre-receive, update et post-receive de tous les dépôts.
dashboard.reinit_missing_repos=Réinitialiser tous les dépôts Git manquants pour lesquels un enregistrement existe
dashboard.sync_external_users=Synchroniser les données de lutilisateur externe
dashboard.cleanup_hook_task_table=Nettoyer la table hook_task

View File

@ -1354,8 +1354,11 @@ editor.this_file_locked=Tá an comhad faoi ghlas
editor.must_be_on_a_branch=Caithfidh tú a bheith ar bhrainse chun athruithe a dhéanamh nó a mholadh ar an gcomhad seo.
editor.fork_before_edit=Ní mór duit an stór seo a fhorcáil chun athruithe a dhéanamh nó a mholadh ar an gcomhad seo.
editor.delete_this_file=Scrios Comhad
editor.delete_this_directory=Scrios Eolaire
editor.must_have_write_access=Caithfidh rochtain scríofa a bheith agat chun athruithe a dhéanamh nó a mholadh ar an gcomhad seo.
editor.file_delete_success=Tá an comhad "%s" scriosta.
editor.directory_delete_success=Scriosadh an eolaire "%s".
editor.delete_directory=Scrios an eolaire '%s'
editor.name_your_file=Ainmnigh do chomhad…
editor.filename_help=Cuir eolaire leis trína ainm a chlóscríobh ina dhiaidh sin le slash ('/'). Bain eolaire trí backspace a chlóscríobh ag tús an réimse ionchuir.
editor.or=
@ -1482,6 +1485,7 @@ projects.column.new_submit=Cruthaigh Colún
projects.column.new=Colún Nua
projects.column.set_default=Socraigh Réamhshocrú
projects.column.set_default_desc=Socraigh an colún seo mar réamhshocrú le haghaidh saincheisteanna agus tarraingtí gan chatagóir
projects.column.default_column_hint=Cuirfear saincheisteanna nua a chuirtear leis an tionscadal seo leis an gcolún seo
projects.column.delete=Scrios Colún
projects.column.deletion_desc=Ag scriosadh colún tionscadail aistríonn gach saincheist ghaolmhar chuig an gcolún. Lean ar aghaidh?
projects.column.color=Dath
@ -3038,7 +3042,7 @@ dashboard.update_migration_poster_id=Nuashonraigh ID póstaer imir
dashboard.git_gc_repos=Bailitheoir bruscair gach stórais
dashboard.resync_all_sshkeys=Nuashonraigh an comhad '.ssh/authorized_keys' le heochracha SSH Gitea
dashboard.resync_all_sshprincipals=Nuashonraigh an comhad '.ssh/authorized_principals' le príomhoidí SSH Gitea
dashboard.resync_all_hooks=Athshioncrónaigh crúcaí réamhghlactha, nuashonraithe agus iarghlactha na stórais uile
dashboard.resync_all_hooks=Athshioncrónaigh crúcaí git na stórtha uile (réamhghlacadh, nuashonrú, iarghlacadh, próiseasghlacadh, ...)
dashboard.reinit_missing_repos=Aththosaigh gach stórais Git atá in easnamh a bhfuil taifid ann dóibh
dashboard.sync_external_users=Sioncrónaigh sonraí úsáideoirí seachtracha
dashboard.cleanup_hook_task_table=Glan suas an tábla hook_task

View File

@ -3038,7 +3038,6 @@ dashboard.update_migration_poster_id=移行する投稿者IDの更新
dashboard.git_gc_repos=すべてのリポジトリでガベージコレクションを実行
dashboard.resync_all_sshkeys='.ssh/authorized_keys' ファイルをGitea上のSSHキーで更新
dashboard.resync_all_sshprincipals='.ssh/authorized_principals' ファイルをGitea上のSSHプリンシパルで更新
dashboard.resync_all_hooks=すべてのリポジトリの pre-receive, update, post-receive フックを再同期する
dashboard.reinit_missing_repos=レコードが存在するが見当たらないすべてのGitリポジトリを再初期化する
dashboard.sync_external_users=外部ユーザーデータの同期
dashboard.cleanup_hook_task_table=hook_taskテーブルのクリーンアップ

View File

@ -1354,8 +1354,11 @@ editor.this_file_locked=Ficheiro bloqueado
editor.must_be_on_a_branch=Tem que estar num ramo para fazer ou propor modificações neste ficheiro.
editor.fork_before_edit=Tem que fazer uma derivação deste repositório para fazer ou propor modificações neste ficheiro.
editor.delete_this_file=Eliminar ficheiro
editor.delete_this_directory=Eliminar pasta
editor.must_have_write_access=Tem que ter permissões de escrita para fazer ou propor modificações neste ficheiro.
editor.file_delete_success=O ficheiro "%s" foi eliminado.
editor.directory_delete_success=A pasta "%s" foi eliminada.
editor.delete_directory=Eliminar a pasta '%s'
editor.name_your_file=Nomeie o seu ficheiro…
editor.filename_help=Adicione uma pasta escrevendo o nome dessa pasta seguido de uma barra('/'). Remova uma pasta carregando na tecla de apagar ('←') no início do campo.
editor.or=ou
@ -1482,6 +1485,7 @@ projects.column.new_submit=Criar coluna
projects.column.new=Nova coluna
projects.column.set_default=Tornar predefinida
projects.column.set_default_desc=Definir esta coluna como a predefinida para questões e pedidos de integração não categorizados
projects.column.default_column_hint=Novas questões adicionadas a este planeamento serão adicionadas a esta coluna
projects.column.delete=Eliminar coluna
projects.column.deletion_desc=Eliminar uma coluna de um planeamento faz com que todas as questões que nela constam sejam movidas para a coluna predefinida. Continuar?
projects.column.color=Colorido
@ -3038,7 +3042,6 @@ dashboard.update_migration_poster_id=Sincronizar os IDs do remetente da migraç
dashboard.git_gc_repos=Fazer a recolha do lixo em todos os repositórios
dashboard.resync_all_sshkeys=Sincronizar o ficheiro '.ssh/authorized_keys' com as chaves SSH do Gitea
dashboard.resync_all_sshprincipals=Modificar o ficheiro '.ssh/authorized_principals' com os protagonistas SSH do Gitea
dashboard.resync_all_hooks=Voltar a sincronizar automatismos de pré-acolhimento, modificação e pós-acolhimento de todos os repositórios
dashboard.reinit_missing_repos=Reinicializar todos os repositórios Git em falta para os quais existam registos
dashboard.sync_external_users=Sincronizar dados externos do utilizador
dashboard.cleanup_hook_task_table=Limpar a tabela hook_task

View File

@ -3032,7 +3032,6 @@ dashboard.update_migration_poster_id=Taşıma poster kimliklerini güncelle
dashboard.git_gc_repos=Tüm depolardaki atıkları temizle
dashboard.resync_all_sshkeys='.ssh/authority_keys' dosyasını Gitea SSH anahtarlarıyla güncelle
dashboard.resync_all_sshprincipals='.ssh/authorized_principals' dosyasını Gitea SSH sorumlularıyla güncelleyin
dashboard.resync_all_hooks=Tüm depoların alma öncesi, güncelleme ve alma sonrası kancalarını yeniden senkronize edin
dashboard.reinit_missing_repos=Kayıtları bulunanlar için tüm eksik Git depolarını yeniden başlat
dashboard.sync_external_users=Harici kullanıcı verisini senkronize et
dashboard.cleanup_hook_task_table=Hook_task tablosunu temizle

View File

@ -426,7 +426,7 @@ need_account=需要一个帐户?
sign_up_tip=您正在系统中注册第一个帐户,它拥有管理员权限。请仔细记住您的用户名和密码。 如果您忘记了用户名或密码,请参阅 Gitea 文档以恢复账户。
sign_up_now=立即注册。
sign_up_successful=帐户创建成功。欢迎!
confirmation_mail_sent_prompt_ex=一封新的确认邮件已经发送到 <b>%s</b>。请在下一个 %s 中检查您的收件箱以完成注册流程。 如果您的注册邮箱地址不正确,您可以重新登录并更改它。
confirmation_mail_sent_prompt_ex=一封新的确认邮件已经发送到 <b>%s</b>。请在 %s 内检查您的收件箱以完成注册流程。 如果您的注册邮箱地址不正确,您可以重新登录并更改它。
must_change_password=更新您的密码
allow_password_change=要求用户更改密码(推荐)
reset_password_mail_sent_prompt=确认邮件已被发送到 <b>%s</b>。请您在 %s 内检查您的收件箱 ,完成密码重置流程。
@ -1483,6 +1483,7 @@ projects.column.new_submit=创建列
projects.column.new=创建列
projects.column.set_default=设为默认
projects.column.set_default_desc=设置此列为未分类问题和合并请求的默认值
projects.column.default_column_hint=添加到此项目的新议题将被添加到此列
projects.column.delete=删除列
projects.column.deletion_desc=删除项目列会将所有相关问题移至默认列。是否继续?
projects.column.color=颜色
@ -1970,6 +1971,9 @@ pulls.status_checks_requested=必须
pulls.status_checks_details=详情
pulls.status_checks_hide_all=隐藏所有检查
pulls.status_checks_show_all=显示所有检查
pulls.status_checks_approve_all=批准所有工作流
pulls.status_checks_need_approvals=%d 个工作流等待批准
pulls.status_checks_need_approvals_helper=此工作流在仓库维护者批准后才会运行。
pulls.update_branch=通过合并更新分支
pulls.update_branch_rebase=通过变基更新分支
pulls.update_branch_success=分支更新成功
@ -3071,7 +3075,7 @@ dashboard.total_gc_time=GC 暂停时间总量
dashboard.total_gc_pause=GC 暂停时间总量
dashboard.last_gc_pause=上次 GC 暂停时间
dashboard.gc_times=GC 执行次数
dashboard.delete_old_actions=从数据库中删除所有旧工作流记录
dashboard.delete_old_actions=从数据库中删除所有旧操作记录
dashboard.delete_old_actions.started=已开始从数据库中删除所有旧工作流记录。
dashboard.update_checker=更新检查器
dashboard.delete_old_system_notices=从数据库中删除所有旧系统通知
@ -3891,6 +3895,7 @@ workflow.has_workflow_dispatch=此工作流有一个 workflow_dispatch 事件触
workflow.has_no_workflow_dispatch=工作流「%s」没有 workflow_dispatch 事件触发器。
need_approval_desc=该工作流由派生仓库的合并请求所触发,需要批准方可运行。
approve_all_success=已成功批准所有工作流运行。
variables=变量
variables.management=变量管理
@ -3911,6 +3916,14 @@ variables.update.success=变量已编辑。
logs.always_auto_scroll=总是自动滚动日志
logs.always_expand_running=总是展开运行日志
general=常规
general.enable_actions=启用工作流
general.collaborative_owners_management=协作所有者管理
general.collaborative_owners_management_help=协作所有者是指其私有仓库有权访问此仓库的工作流的用户或组织。
general.add_collaborative_owner=添加协作所有者
general.collaborative_owner_not_exist=协作所有者不存在。
general.remove_collaborative_owner=移除协作所有者
general.remove_collaborative_owner_desc=移除协作所有者将阻止该所有者的其他仓库访问此仓库中的工作流。是否继续?
[projects]
deleted.display_name=已删除项目

View File

@ -1,6 +1,6 @@
{
"type": "module",
"packageManager": "pnpm@10.19.0",
"packageManager": "pnpm@10.23.0",
"engines": {
"node": ">= 22.6.0",
"pnpm": ">= 10.0.0"
@ -12,10 +12,10 @@
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
"@github/paste-markdown": "1.5.3",
"@github/relative-time-element": "4.5.0",
"@github/relative-time-element": "4.5.1",
"@github/text-expander-element": "2.9.2",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.19.0",
"@primer/octicons": "19.21.0",
"@resvg/resvg-wasm": "2.6.2",
"@silverwind/vue3-calendar-heatmap": "2.0.6",
"@techknowlogick/license-checker-webpack-plugin": "0.3.0",
@ -25,10 +25,10 @@
"chart.js": "4.5.1",
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
"clippie": "4.1.8",
"clippie": "4.1.9",
"cropperjs": "1.6.2",
"css-loader": "7.1.2",
"dayjs": "1.11.18",
"dayjs": "1.11.19",
"dropzone": "6.0.0-beta.2",
"easymde": "2.20.0",
"esbuild-loader": "4.4.0",
@ -38,7 +38,7 @@
"katex": "0.16.25",
"mermaid": "11.12.1",
"mini-css-extract-plugin": "2.9.4",
"monaco-editor": "0.54.0",
"monaco-editor": "0.55.1",
"monaco-editor-webpack-plugin": "7.1.1",
"online-3d-viewer": "0.16.0",
"pdfobject": "2.3.1",
@ -46,7 +46,7 @@
"postcss": "8.5.6",
"postcss-loader": "8.2.0",
"sortablejs": "1.15.6",
"swagger-ui-dist": "5.30.0",
"swagger-ui-dist": "5.30.3",
"tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
@ -56,18 +56,18 @@
"typescript": "5.9.3",
"uint8-to-base64": "0.2.1",
"vanilla-colorful": "0.7.2",
"vue": "3.5.22",
"vue": "3.5.25",
"vue-bar-graph": "2.2.0",
"vue-chartjs": "5.3.2",
"vue-chartjs": "5.3.3",
"vue-loader": "17.4.2",
"webpack": "5.102.1",
"webpack": "5.103.0",
"webpack-cli": "6.0.1",
"wrap-ansi": "9.0.2"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
"@playwright/test": "1.56.1",
"@stylistic/eslint-plugin": "5.5.0",
"@playwright/test": "1.57.0",
"@stylistic/eslint-plugin": "5.6.1",
"@stylistic/stylelint-plugin": "4.0.0",
"@types/codemirror": "5.60.17",
"@types/dropzone": "5.7.9",
@ -79,40 +79,39 @@
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/toastify-js": "1.12.4",
"@typescript-eslint/parser": "8.46.2",
"@vitejs/plugin-vue": "6.0.1",
"@vitest/eslint-plugin": "1.3.26",
"eslint": "9.38.0",
"@typescript-eslint/parser": "8.48.0",
"@vitejs/plugin-vue": "6.0.2",
"@vitest/eslint-plugin": "1.5.0",
"eslint": "9.39.1",
"eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-array-func": "5.1.0",
"eslint-plugin-github": "6.0.0",
"eslint-plugin-import-x": "4.16.1",
"eslint-plugin-no-use-extend-native": "0.7.2",
"eslint-plugin-playwright": "2.2.2",
"eslint-plugin-playwright": "2.3.0",
"eslint-plugin-regexp": "2.10.0",
"eslint-plugin-sonarjs": "3.0.5",
"eslint-plugin-unicorn": "62.0.0",
"eslint-plugin-vue": "10.5.1",
"eslint-plugin-vue": "10.6.1",
"eslint-plugin-vue-scoped-css": "2.12.0",
"eslint-plugin-wc": "3.0.2",
"globals": "16.4.0",
"happy-dom": "20.0.8",
"markdownlint-cli": "0.45.0",
"material-icon-theme": "5.28.0",
"globals": "16.5.0",
"happy-dom": "20.0.10",
"markdownlint-cli": "0.46.0",
"material-icon-theme": "5.29.0",
"nolyfill": "1.0.44",
"postcss-html": "1.8.0",
"spectral-cli-bundle": "1.0.3",
"stylelint": "16.25.0",
"stylelint": "16.26.0",
"stylelint-config-recommended": "17.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-declaration-strict-value": "1.10.11",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "4.0.0",
"typescript-eslint": "8.46.2",
"updates": "16.9.1",
"vite-string-plugin": "1.4.6",
"vitest": "4.0.4",
"vue-tsc": "3.1.2"
"typescript-eslint": "8.48.0",
"updates": "16.9.2",
"vite-string-plugin": "1.4.9",
"vitest": "4.0.14",
"vue-tsc": "3.1.5"
},
"browserslist": [
"defaults"

2025
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-boolean-off" width="16" height="16" aria-hidden="true"><path d="M13.25 1c.966 0 1.75.784 1.75 1.75v10.5c0 .464-.184.909-.513 1.237A1.75 1.75 0 0 1 13.25 15H2.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 1 13.25V2.75C1 1.784 1.784 1 2.75 1zM2.75 2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25V2.75a.25.25 0 0 0-.25-.25z"/><path d="M9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8M11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/></svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-boolean-on" width="16" height="16" aria-hidden="true"><path d="M13.25 1c.966 0 1.75.784 1.75 1.75v10.5c0 .464-.184.909-.513 1.237A1.75 1.75 0 0 1 13.25 15H2.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 1 13.25V2.75C1 1.784 1.784 1 2.75 1zM2.75 2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25V2.75a.25.25 0 0 0-.25-.25z"/><path d="M8.75 5.75a.75.75 0 0 0-1.5 0v4.5a.75.75 0 0 0 1.5 0z"/></svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-compose" width="16" height="16" aria-hidden="true"><path d="m14.515.456.965.965a1.555 1.555 0 0 1 0 2.2L9.745 9.355a1.55 1.55 0 0 1-.672.396l-2.89.826a.67.67 0 0 1-.828-.474.66.66 0 0 1 .004-.35l.825-2.89c.073-.254.209-.486.396-.673L12.315.456c.144-.145.316-.259.505-.337a1.54 1.54 0 0 1 1.19 0c.189.078.361.192.505.337m-3.322 3.008-3.67 3.669a.2.2 0 0 0-.057.096L6.97 8.965l1.736-.496a.2.2 0 0 0 .096-.056l3.67-3.67Zm2.065-2.066L12.135 2.52l1.28 1.28 1.122-1.122a.22.22 0 0 0 .065-.157.22.22 0 0 0-.065-.157l-.965-.966a.22.22 0 0 0-.157-.065.23.23 0 0 0-.157.065"/><path d="M0 14.25V2.75A1.75 1.75 0 0 1 1.75 1H7a.75.75 0 0 1 0 1.5H1.75a.25.25 0 0 0-.25.25v11.5a.25.25 0 0 0 .25.25h11.5a.25.25 0 0 0 .25-.25V8.5a.75.75 0 0 1 1.5 0v5.75c0 .464-.184.909-.513 1.237A1.75 1.75 0 0 1 13.25 16H1.75A1.75 1.75 0 0 1 0 14.25"/></svg>

After

Width:  |  Height:  |  Size: 905 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-crosshairs" width="16" height="16" aria-hidden="true"><path d="M14 8A6 6 0 1 1 2 8a6 6 0 0 1 12 0m-1.5 0a4.5 4.5 0 1 0-9 0 4.5 4.5 0 0 0 9 0"/><path d="M5 7.25a.75.75 0 0 1 0 1.5H1a.75.75 0 0 1 0-1.5Zm3-7a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0V1A.75.75 0 0 1 8 .25m7 7a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1 0-1.5Zm-7 3a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0v-4a.75.75 0 0 1 .75-.75"/></svg>

After

Width:  |  Height:  |  Size: 470 B

1
public/assets/img/svg/octicon-dice.svg generated Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-dice" width="16" height="16" aria-hidden="true"><path d="M13.25 1c.966 0 1.75.784 1.75 1.75v10.5c0 .464-.184.909-.513 1.237A1.75 1.75 0 0 1 13.25 15H2.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 1 13.25V2.75C1 1.784 1.784 1 2.75 1zM2.75 2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25V2.75a.25.25 0 0 0-.25-.25z"/><path d="M5 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2m6-6a1 1 0 1 0 0-2 1 1 0 0 0 0 2M8 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-exclamation" width="16" height="16" aria-hidden="true"><path d="M8 11a2 2 0 1 1 .001 3.999A2 2 0 0 1 8 11M8 1a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-3 0v-6A1.5 1.5 0 0 1 8 1"/></svg>

After

Width:  |  Height:  |  Size: 260 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-file-check" width="16" height="16" aria-hidden="true"><path d="M10.336 0c.464 0 .91.184 1.237.513l2.914 2.914c.33.328.513.773.513 1.237v3.587c0 .199-.079.39-.22.53a.747.747 0 0 1-1.06 0 .75.75 0 0 1-.22-.53V6h-2.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 9 4.25V1.5H3.75a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25H7c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06A.75.75 0 0 1 7 16H3.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 2 14.25V1.75C2 .784 2.784 0 3.75 0Zm.164 4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"/><path d="M15.259 10a.75.75 0 0 1 .686.472.75.75 0 0 1-.171.815l-4.557 4.45a.75.75 0 0 1-1.055-.01L8.22 13.778a.754.754 0 0 1 .04-1.02.75.75 0 0 1 1.02-.038l1.42 1.425 4.025-3.932a.75.75 0 0 1 .534-.213"/></svg>

After

Width:  |  Height:  |  Size: 831 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-flowchart" width="16" height="16" aria-hidden="true"><path d="M9.25 5c.199 0 .39-.079.53-.22a.747.747 0 0 0 0-1.06.75.75 0 0 0-.53-.22zm-2-1.5a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06c.14.141.331.22.53.22zm7 8.75.53.53c.3-.3.3-.77 0-1.06zm-1.47-2.53a.748.748 0 0 0-1.244.23.76.76 0 0 0 .184.83l.53-.53zm-1.06 4a.748.748 0 0 0 .23 1.244.76.76 0 0 0 .83-.184l-.53-.53zm1.53-.72c.199 0 .39-.079.53-.22a.747.747 0 0 0 0-1.06.75.75 0 0 0-.53-.22zm-6-1.5a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06c.14.141.331.22.53.22zm3.53-4.72.53-.53-1.06-1.06-.53.53.53.53zM5.72 9.72l-.53.53 1.06 1.06.53-.53-.53-.53zm3.53-5.47V3.5h-2V5h2zm5 8 .53-.53-2-2-.53.53-.53.53 2 2zm0 0-.53-.53-2 2 .53.53.53.53 2-2zm-1 0v-.75h-6V13h6zm-3-6-.53-.53-4 4 .53.53.53.53 4-4zM3.25 2v.75h2v-1.5h-2zm2 0v.75c.28 0 .5.22.5.5h1.5a2 2 0 0 0-2-2zM6.5 3.25h-.75v2h1.5v-2zm0 2h-.75c0 .133-.053.26-.146.354a.5.5 0 0 1-.354.146v1.5a2 2 0 0 0 2-2zM5.25 6.5v-.75h-2v1.5h2zm-2 0v-.75a.5.5 0 0 1-.354-.146.5.5 0 0 1-.146-.354h-1.5c0 1.1.9 2 2 2zM2 5.25h.75v-2h-1.5v2zm0-2h.75c0-.28.22-.5.5-.5v-1.5a2 2 0 0 0-2 2zM11.25 2v.75h2v-1.5h-2zm2 0v.75c.28 0 .5.22.5.5h1.5a2 2 0 0 0-2-2zm1.25 1.25h-.75v2h1.5v-2zm0 2h-.75c0 .133-.053.26-.146.354a.5.5 0 0 1-.354.146v1.5a2 2 0 0 0 2-2zM13.25 6.5v-.75h-2v1.5h2zm-2 0v-.75a.5.5 0 0 1-.354-.146.5.5 0 0 1-.146-.354h-1.5c0 1.1.9 2 2 2zM10 5.25h.75v-2h-1.5v2zm0-2h.75c0-.28.22-.5.5-.5v-1.5a2 2 0 0 0-2 2zM3.25 10v.75h2v-1.5h-2zm2 0v.75c.28 0 .5.22.5.5h1.5a2 2 0 0 0-2-2zm1.25 1.25h-.75v2h1.5v-2zm0 2h-.75c0 .133-.053.26-.146.354a.5.5 0 0 1-.354.146v1.5a2 2 0 0 0 2-2zM5.25 14.5v-.75h-2v1.5h2zm-2 0v-.75a.5.5 0 0 1-.354-.146.5.5 0 0 1-.146-.354h-1.5c0 1.1.9 2 2 2zM2 13.25h.75v-2h-1.5v2zm0-2h.75c0-.28.22-.5.5-.5v-1.5a2 2 0 0 0-2 2z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-focus-center" width="16" height="16" aria-hidden="true"><path d="M2.75 2.5a.25.25 0 0 0-.25.25v2.5a.75.75 0 0 1-1.5 0v-2.5C1 1.784 1.784 1 2.75 1h2.5a.75.75 0 0 1 0 1.5zM10 1.75a.75.75 0 0 1 .75-.75h2.5c.966 0 1.75.784 1.75 1.75v2.5a.75.75 0 0 1-1.5 0v-2.5a.25.25 0 0 0-.25-.25h-2.5a.75.75 0 0 1-.75-.75M1.75 10a.75.75 0 0 1 .75.75v2.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 0 1.5h-2.5A1.75 1.75 0 0 1 1 13.25v-2.5a.75.75 0 0 1 .75-.75m12.5 0a.75.75 0 0 1 .75.75v2.5A1.75 1.75 0 0 1 13.25 15h-2.5a.75.75 0 0 1 0-1.5h2.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 .75-.75M8 10a2 2 0 1 0 .001-3.999A2 2 0 0 0 8 10"/><path d="M8 10a2 2 0 1 0 .001-3.999A2 2 0 0 0 8 10"/></svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-git-branch-check" width="16" height="16" aria-hidden="true"><path d="M15.26 10a.74.74 0 0 1 .414.133.75.75 0 0 1 .1 1.154l-4.557 4.45a.753.753 0 0 1-1.055-.008l-1.943-1.95a.755.755 0 0 1 .024-1.038.753.753 0 0 1 1.038-.022l1.42 1.427 4.026-3.933A.75.75 0 0 1 15.26 10m-3.51-9a2.252 2.252 0 0 1 1.942 3.389 2.25 2.25 0 0 1-1.192.983V6A2.5 2.5 0 0 1 10 8.5H6a.997.997 0 0 0-1 1v1.128a2.256 2.256 0 0 1 1.469 2.503A2.252 2.252 0 1 1 3.5 10.628V5.372a2.255 2.255 0 0 1-1.469-2.503A2.252 2.252 0 1 1 5 5.372v1.836A2.5 2.5 0 0 1 6 7h4a.997.997 0 0 0 1-1v-.628A2.252 2.252 0 0 1 11.75 1m-7.5 1.5a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06.747.747 0 0 0 1.06 0 .747.747 0 0 0 0-1.06.75.75 0 0 0-.53-.22m0 9.5a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06.747.747 0 0 0 1.06 0 .747.747 0 0 0 0-1.06.75.75 0 0 0-.53-.22m7.5-9.5a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06.747.747 0 0 0 1.06 0 .747.747 0 0 0 0-1.06.75.75 0 0 0-.53-.22"/></svg>

After

Width:  |  Height:  |  Size: 1002 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-graph-bar-horizontal" width="16" height="16" aria-hidden="true"><path d="M15.25 15H.75q-.311 0-.53-.22-.22-.219-.22-.53t.22-.53q.219-.22.53-.22h14.5q.311 0 .53.22.22.219.22.53t-.22.53q-.219.22-.53.22"/><path d="M2.25 7h2.5a.25.25 0 0 1 .25.25v6.5a.25.25 0 0 1-.25.25h-2.5a.25.25 0 0 1-.25-.25v-6.5A.25.25 0 0 1 2.25 7m4-4h2.5a.25.25 0 0 1 .25.25v10.5a.25.25 0 0 1-.25.25h-2.5a.25.25 0 0 1-.25-.25V3.25A.25.25 0 0 1 6.25 3m4 6h2.5a.25.25 0 0 1 .25.25v4.5a.25.25 0 0 1-.25.25h-2.5a.25.25 0 0 1-.25-.25v-4.5a.25.25 0 0 1 .25-.25"/></svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-graph-bar-vertical" width="16" height="16" aria-hidden="true"><path d="M1 15.25V.75q0-.311.22-.53.219-.22.53-.22t.53.22q.22.219.22.53v14.5q0 .311-.22.53-.219.22-.53.22t-.53-.22Q1 15.561 1 15.25"/><path d="M9 3.25v2.5a.25.25 0 0 1-.25.25h-6.5A.25.25 0 0 1 2 5.75v-2.5A.25.25 0 0 1 2.25 3h6.5a.25.25 0 0 1 .25.25m4 4v2.5a.25.25 0 0 1-.25.25H2.25A.25.25 0 0 1 2 9.75v-2.5A.25.25 0 0 1 2.25 7h10.5a.25.25 0 0 1 .25.25m-6 4v2.5a.25.25 0 0 1-.25.25h-4.5a.25.25 0 0 1-.25-.25v-2.5a.25.25 0 0 1 .25-.25h4.5a.25.25 0 0 1 .25.25"/></svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-inbox-fill" width="16" height="16" aria-hidden="true"><path d="M2.8 2.06A1.75 1.75 0 0 1 4.41 1h7.18c.7 0 1.333.417 1.61 1.06l2.74 6.395a.8.8 0 0 1 .06.295v4.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25v-4.5a.8.8 0 0 1 .06-.295zm1.61.44a.25.25 0 0 0-.23.152L1.887 8H4.75a.75.75 0 0 1 .6.3L6.625 10h2.75l1.275-1.7a.75.75 0 0 1 .6-.3h2.863L11.82 2.652a.25.25 0 0 0-.23-.152z"/></svg>

After

Width:  |  Height:  |  Size: 471 B

1
public/assets/img/svg/octicon-node.svg generated Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-node" width="16" height="16" aria-hidden="true"><path d="M8 5a1.5 1.5 0 0 0 1.061-.439 1.507 1.507 0 0 0 0-2.122 1.507 1.507 0 0 0-2.122 0 1.503 1.503 0 0 0 0 2.122C7.221 4.842 7.602 5 8 5m0 9a1.5 1.5 0 0 0 1.061-.439 1.507 1.507 0 0 0 0-2.122 1.507 1.507 0 0 0-2.122 0 1.503 1.503 0 0 0 0 2.122c.282.281.663.439 1.061.439m-7-2.5v-7a1.75 1.75 0 0 1 1.75-1.75H4.5a.75.75 0 0 1 0 1.5H2.75a.25.25 0 0 0-.25.25v7l.005.049a.246.246 0 0 0 .245.201H4.5a.75.75 0 0 1 0 1.5H2.75A1.75 1.75 0 0 1 1 11.5m12.5 0v-7a.25.25 0 0 0-.201-.245l-.049-.005H11.5a.75.75 0 0 1 0-1.5h1.75A1.75 1.75 0 0 1 15 4.5v7c0 .464-.184.909-.513 1.237a1.75 1.75 0 0 1-1.237.513H11.5a.75.75 0 0 1 0-1.5h1.75a.25.25 0 0 0 .25-.25"/></svg>

After

Width:  |  Height:  |  Size: 781 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-pencil-ai" width="16" height="16" aria-hidden="true"><path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.609 8.61c-.21.21-.471.363-.757.445l-3.251.929a.75.75 0 0 1-.736-.191.75.75 0 0 1-.191-.736l.929-3.251a1.76 1.76 0 0 1 .445-.757Zm-7.549 9.67a.25.25 0 0 0-.064.108l-.558 1.953 1.953-.558a.25.25 0 0 0 .108-.064l6.286-6.286L9.75 4.811Zm8.963-8.61a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Zm-.158 6.676A.25.25 0 0 1 12.502 9a.25.25 0 0 1 .232.163l.238.648a3.72 3.72 0 0 0 2.219 2.219l.649.238a.25.25 0 0 1 .16.202v.063a.25.25 0 0 1-.16.202l-.649.238a3.72 3.72 0 0 0-2.219 2.218l-.238.649a.25.25 0 0 1-.193.16h-.079a.25.25 0 0 1-.193-.16l-.239-.649a3.74 3.74 0 0 0-2.218-2.218l-.649-.238a.248.248 0 0 1-.118-.376.25.25 0 0 1 .118-.091l.649-.238a3.72 3.72 0 0 0 2.218-2.219Z"/></svg>

After

Width:  |  Height:  |  Size: 928 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-smiley-frown" width="16" height="16" aria-hidden="true"><path d="M8 0a7.996 7.996 0 0 1 8 8 8 8 0 1 1-8-8m0 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13m0 7.996a3.78 3.78 0 0 1 2.127.629q.289.195.53.445.1.1.184.213l.015.019.004.008.002.002.001.002v.001a1 1 0 0 1-.07.05l.071-.05a.753.753 0 0 1-.175 1.046.75.75 0 0 1-1.047-.175l-.007-.009a1.8 1.8 0 0 0-.35-.31c-.265-.179-.683-.371-1.285-.371s-1.021.192-1.285.37a1.8 1.8 0 0 0-.35.31l-.007.01a.747.747 0 0 1-1.038.174h-.001a.75.75 0 0 1-.183-1.044l.614.43-.612-.432v-.002l.002-.002.005-.007.014-.02a3.3 3.3 0 0 1 .715-.657c.474-.322 1.18-.63 2.126-.63M5 6a1 1 0 1 1 0 1.998A1 1 0 0 1 5 6m6 0a1 1 0 1 1 0 1.998A1 1 0 0 1 11 6"/></svg>

After

Width:  |  Height:  |  Size: 759 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-smiley-frustrated" width="16" height="16" aria-hidden="true"><path d="M8 0a7.996 7.996 0 0 1 8 8 8 8 0 1 1-8-8m0 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13m0 6.75c2.487 0 3.518 1.98 3.727 2.818A.75.75 0 0 1 11 12H5a.75.75 0 0 1-.728-.932C4.482 10.23 5.513 8.25 8 8.25m3.259-3.854a.651.651 0 0 1 .482 1.208L10.75 6l.991.396a.651.651 0 0 1-.482 1.208L8.99 6.696a.75.75 0 0 1 0-1.392Zm-7.363.363a.65.65 0 0 1 .845-.363l2.268.908a.75.75 0 0 1 0 1.392l-2.268.908a.651.651 0 0 1-.483-1.208L5.25 6l-.992-.396a.65.65 0 0 1-.362-.845M8 9.75c-.822 0-1.383.351-1.746.75h3.492c-.363-.399-.924-.75-1.746-.75"/></svg>

After

Width:  |  Height:  |  Size: 681 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-smiley-grin" width="16" height="16" aria-hidden="true"><path d="M8 0a7.996 7.996 0 0 1 8 8 8 8 0 1 1-8-8m0 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13m3 7.75a.75.75 0 0 1 .727.932C11.518 11.02 10.487 13 8 13s-3.518-1.98-3.728-2.818A.75.75 0 0 1 5 9.25ZM8.329 6.164c.895-1.788 3.447-1.788 4.342 0a.75.75 0 0 1-1.342.671.927.927 0 0 0-1.658 0 .75.75 0 0 1-1.342-.671m-5 0c.895-1.788 3.447-1.788 4.342 0a.75.75 0 0 1-1.342.671.927.927 0 0 0-1.658 0 .75.75 0 0 1-1.342-.671m2.925 4.586c.363.399.924.75 1.746.75s1.383-.351 1.746-.75Z"/></svg>

After

Width:  |  Height:  |  Size: 614 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-smiley-neutral" width="16" height="16" aria-hidden="true"><path d="M8 0a7.996 7.996 0 0 1 8 8 8 8 0 1 1-8-8m0 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13m2 8.75a.75.75 0 0 1 0 1.5H6a.75.75 0 0 1 0-1.5ZM5 6a1 1 0 1 1 0 1.998A1 1 0 0 1 5 6m6 0a1 1 0 1 1 0 1.998A1 1 0 0 1 11 6"/></svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-spacing-large" width="16" height="16" aria-hidden="true"><path d="M13.25 2H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5m-3 5h-4.5a.75.75 0 0 0 0 1.5h4.5a.75.75 0 0 0 0-1.5m3 5H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5"/></svg>

After

Width:  |  Height:  |  Size: 314 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-spacing-medium" width="16" height="16" aria-hidden="true"><path d="M13.25 3H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5m-3 4h-4.5a.75.75 0 0 0 0 1.5h4.5a.75.75 0 0 0 0-1.5m3 4H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5"/></svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-spacing-small" width="16" height="16" aria-hidden="true"><path d="M13.25 4H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5m-3 3h-4.5a.75.75 0 0 0 0 1.5h4.5a.75.75 0 0 0 0-1.5m3 3H2.75a.75.75 0 0 0 0 1.5h10.5a.75.75 0 0 0 0-1.5"/></svg>

After

Width:  |  Height:  |  Size: 314 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-split-view" width="16" height="16" aria-hidden="true"><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5c0 .464-.184.909-.513 1.237A1.75 1.75 0 0 1 14.25 16H1.75c-.464 0-.909-.184-1.237-.513A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0M1.5 1.75v12.5c0 .138.112.25.25.25H7.5v-13H1.75a.25.25 0 0 0-.25.25M9 14.5h5.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H9Z"/></svg>

After

Width:  |  Height:  |  Size: 457 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-unwrap" width="16" height="16" aria-hidden="true"><path d="M1 2.75c0-.199.079-.39.22-.53A.75.75 0 0 1 1.75 2h12.5c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06.75.75 0 0 1-.53.22H1.75a.75.75 0 0 1-.53-.22.75.75 0 0 1-.22-.53m0 5c0-.199.079-.39.22-.53A.75.75 0 0 1 1.75 7h12.5c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06.75.75 0 0 1-.53.22H1.75a.75.75 0 0 1-.53-.22.75.75 0 0 1-.22-.53M1.75 12h3.5c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06.75.75 0 0 1-.53.22h-3.5a.75.75 0 0 1-.53-.22.747.747 0 0 1 0-1.06.75.75 0 0 1 .53-.22"/></svg>

After

Width:  |  Height:  |  Size: 609 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-vscode" width="16" height="16" aria-hidden="true"><path d="M12.515.537c1.169-1.215 3.48-.226 3.418 1.534a593 593 0 0 1 .062 11.538c.089 1.938-2.439 3.149-3.827 1.851A643 643 0 0 1 1.312 5.996a.93.93 0 0 1-.308-.609.92.92 0 0 1 .194-.655.87.87 0 0 1 1.232-.136l1.493 1.18a641 641 0 0 1 9.708 7.85c.008.011.036-.018.019-.017a606 606 0 0 1 .057-11.226c-1.308 1.157-2.63 2.275-3.926 3.411-.477.416-.948.831-1.424 1.253a.87.87 0 0 1-1.237-.061.9.9 0 0 1-.231-.641.94.94 0 0 1 .27-.628c.452-.456.902-.905 1.36-1.354 1.324-1.302 2.677-2.558 3.996-3.826M2.986 9.734a.8.8 0 0 1 1.184.06.95.95 0 0 1-.057 1.272l-1.228 1.2a.8.8 0 0 1-1.183-.06.95.95 0 0 1 .055-1.272z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" viewBox="0 0 16 16" class="svg octicon-vscode" width="16" height="16"><path d="M10.863 13.919a.8.8 0 0 1-.644.025.8.8 0 0 1-.279-.183L4.816 9.063l-2.232 1.703a.54.54 0 0 1-.691-.031l-.716-.655a.546.546 0 0 1 0-.805L3.112 7.5 1.177 5.725a.546.546 0 0 1 0-.805l.716-.655a.54.54 0 0 1 .691-.031l2.232 1.703L9.94 1.239a.805.805 0 0 1 .923-.159l2.677 1.295c.281.136.46.422.46.736V8h-3.248V4.534L6.864 7.5l3.888 2.966V8H14v3.889c0 .314-.179.6-.46.736z"/></svg>

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 513 B

1
public/assets/img/svg/octicon-wrap.svg generated Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-wrap" width="16" height="16" aria-hidden="true"><path d="M1.75 7a.75.75 0 0 0-.53.22.747.747 0 0 0 0 1.06c.14.141.331.22.53.22h10c.464 0 .909.184 1.237.513a1.746 1.746 0 0 1 0 2.474A1.75 1.75 0 0 1 11.75 12h-1.464v-.464a.68.68 0 0 0-.375-.607.69.69 0 0 0-.711.064l-1.619 1.214a.68.68 0 0 0 0 1.086L9.2 14.507a.68.68 0 0 0 .711.064.69.69 0 0 0 .375-.607V13.5h1.464A3.247 3.247 0 0 0 15 10.25 3.247 3.247 0 0 0 11.75 7zM1 2.75c0-.199.079-.39.22-.53A.75.75 0 0 1 1.75 2h12.5c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06.75.75 0 0 1-.53.22H1.75a.75.75 0 0 1-.53-.22.75.75 0 0 1-.22-.53M1.75 12h3.5c.199 0 .39.079.53.22a.747.747 0 0 1 0 1.06.75.75 0 0 1-.53.22h-3.5a.75.75 0 0 1-.53-.22.747.747 0 0 1 0-1.06.75.75 0 0 1 .53-.22"/></svg>

After

Width:  |  Height:  |  Size: 808 B

View File

@ -5,12 +5,10 @@ package composer
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
@ -23,8 +21,6 @@ import (
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
)
func apiError(ctx *context.Context, status int, obj any) {
@ -193,7 +189,7 @@ func UploadPackage(ctx *context.Context) {
}
defer buf.Close()
cp, err := composer_module.ParsePackage(buf, buf.Size())
cp, err := composer_module.ParsePackage(buf, ctx.FormTrim("version"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
apiError(ctx, http.StatusBadRequest, err)
@ -209,12 +205,9 @@ func UploadPackage(ctx *context.Context) {
}
if cp.Version == "" {
v, err := version.NewVersion(ctx.FormTrim("version"))
if err != nil {
apiError(ctx, http.StatusBadRequest, composer_module.ErrInvalidVersion)
return
}
cp.Version = v.String()
// the version should be either set in the "composer.json", or as a query parameter "?version=xxx"
apiError(ctx, http.StatusBadRequest, composer_module.ErrInvalidVersion)
return
}
_, _, err = packages_service.CreatePackageAndAddFile(
@ -235,7 +228,7 @@ func UploadPackage(ctx *context.Context) {
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: strings.ToLower(fmt.Sprintf("%s.%s.zip", strings.ReplaceAll(cp.Name, "/", "-"), cp.Version)),
Filename: cp.Filename,
},
Creator: ctx.Doer,
Data: buf,

View File

@ -290,8 +290,8 @@ func PostBlobsUploads(ctx *context.Context) {
Creator: ctx.Doer,
},
); err != nil {
switch err {
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
switch {
case errors.Is(err, packages_service.ErrQuotaTotalCount), errors.Is(err, packages_service.ErrQuotaTypeSize), errors.Is(err, packages_service.ErrQuotaTotalSize):
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
@ -439,8 +439,8 @@ func PutBlobsUpload(ctx *context.Context) {
Creator: ctx.Doer,
},
); err != nil {
switch err {
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
switch {
case errors.Is(err, packages_service.ErrQuotaTotalCount), errors.Is(err, packages_service.ErrQuotaTypeSize), errors.Is(err, packages_service.ErrQuotaTotalSize):
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
@ -592,13 +592,10 @@ func PutManifest(ctx *context.Context) {
apiErrorDefined(ctx, namedError)
} else if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errBlobUnknown)
} else if errors.Is(err, packages_service.ErrQuotaTotalCount) || errors.Is(err, packages_service.ErrQuotaTypeSize) || errors.Is(err, packages_service.ErrQuotaTotalSize) {
apiError(ctx, http.StatusForbidden, err)
} else {
switch err {
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
apiError(ctx, http.StatusInternalServerError, err)
}
return
}

View File

@ -82,9 +82,11 @@ type processManifestTxRet struct {
}
func handleCreateManifestResult(ctx context.Context, err error, mci *manifestCreationInfo, contentStore *packages_module.ContentStore, txRet *processManifestTxRet) (string, error) {
if err != nil && txRet.created && txRet.pb != nil {
if err := contentStore.Delete(packages_module.BlobHash256Key(txRet.pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
if err != nil {
if txRet.created && txRet.pb != nil {
if err := contentStore.Delete(packages_module.BlobHash256Key(txRet.pb.HashSHA256)); err != nil {
log.Error("Error deleting package blob from content store: %v", err)
}
}
return "", err
}
@ -198,14 +200,14 @@ func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *p
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
return errManifestBlobUnknown
}
return err
return fmt.Errorf("GetContainerBlob: %w", err)
}
size, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
VersionID: pfd.File.VersionID,
})
if err != nil {
return err
return fmt.Errorf("CalculateFileSize: %w", err)
}
metadata.Manifests = append(metadata.Manifests, &container_module.Manifest{
@ -217,7 +219,7 @@ func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *p
pv, err := createPackageAndVersion(ctx, mci, metadata)
if err != nil {
return err
return fmt.Errorf("createPackageAndVersion: %w", err)
}
txRet.pv = pv
@ -240,7 +242,7 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return nil, err
return nil, fmt.Errorf("TryInsertPackage: %w", err)
}
created = false
}
@ -248,7 +250,7 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(mci.Owner.LowerName+"/"+mci.Image)); err != nil {
log.Error("Error setting package property: %v", err)
return nil, err
return nil, fmt.Errorf("InsertProperty(PropertyRepository): %w", err)
}
}
@ -256,7 +258,7 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
metadataJSON, err := json.Marshal(metadata)
if err != nil {
return nil, err
return nil, fmt.Errorf("json.Marshal(metadata): %w", err)
}
// "docker buildx imagetools create" multi-arch operations:
@ -276,43 +278,43 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
pv, err := packages_model.GetOrInsertVersion(ctx, _pv)
if err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return nil, err
log.Error("Error GetOrInsertVersion (first try) package: %v", err)
return nil, fmt.Errorf("GetOrInsertVersion: first try: %w", err)
}
if err = packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
return nil, err
return nil, fmt.Errorf("DeletePackageVersionAndReferences: %w", err)
}
// keep download count on overwriting
_pv.DownloadCount = pv.DownloadCount
pv, err = packages_model.GetOrInsertVersion(ctx, _pv)
if err != nil {
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return nil, err
log.Error("Error GetOrInsertVersion (second try) package: %v", err)
return nil, fmt.Errorf("GetOrInsertVersion: second try: %w", err)
}
}
}
if err := packages_service.CheckCountQuotaExceeded(ctx, mci.Creator, mci.Owner); err != nil {
return nil, err
return nil, fmt.Errorf("CheckCountQuotaExceeded: %w", err)
}
if mci.IsTagged {
if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
return nil, err
return nil, fmt.Errorf("InsertOrUpdateProperty(ManifestTagged): %w", err)
}
} else {
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged); err != nil {
return nil, err
return nil, fmt.Errorf("DeletePropertiesByName(ManifestTagged): %w", err)
}
}
if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference); err != nil {
return nil, err
return nil, fmt.Errorf("DeletePropertiesByName(ManifestReference): %w", err)
}
for _, manifest := range metadata.Manifests {
if _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
return nil, err
return nil, fmt.Errorf("InsertProperty(ManifestReference): %w", err)
}
}

View File

@ -480,7 +480,7 @@ func RenameUser(ctx *context.APIContext) {
newName := web.GetForm(ctx).(*api.RenameUserOption).NewName
// Check if username has been changed
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName, ctx.Doer); err != nil {
if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
ctx.APIError(http.StatusUnprocessableEntity, err)
} else {

View File

@ -81,6 +81,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/activitypub"
"code.gitea.io/gitea/routers/api/v1/admin"
@ -774,7 +775,9 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) {
return func(ctx *context.APIContext) {
ar, err := common.AuthShared(ctx.Base, nil, authMethod)
if err != nil {
ctx.APIError(http.StatusUnauthorized, err)
msg, ok := auth.ErrAsUserAuthMessage(err)
msg = util.Iif(ok, msg, "invalid username, password or token")
ctx.APIError(http.StatusUnauthorized, msg)
return
}
ctx.Doer = ar.Doer

View File

@ -340,7 +340,7 @@ func Rename(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.RenameOrgOption)
orgUser := ctx.Org.Organization.AsUser()
if err := user_service.RenameUser(ctx, orgUser, form.NewName); err != nil {
if err := user_service.RenameUser(ctx, orgUser, form.NewName, ctx.Doer); err != nil {
if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
ctx.APIError(http.StatusUnprocessableEntity, err)
} else {

View File

@ -610,10 +610,6 @@ func handleChangeRepoFilesError(ctx *context.APIContext, err error) {
ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
if git.IsErrBranchNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
ctx.APIError(http.StatusNotFound, err)
return
}
if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
return

View File

@ -201,7 +201,7 @@ func CreateIssueDependency(ctx *context.APIContext) {
return
}
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
dependencyPerm := getPermissionForRepo(ctx, dependency.Repo)
if ctx.Written() {
return
}
@ -262,7 +262,7 @@ func RemoveIssueDependency(ctx *context.APIContext) {
return
}
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
dependencyPerm := getPermissionForRepo(ctx, dependency.Repo)
if ctx.Written() {
return
}

View File

@ -7,6 +7,7 @@ import (
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
release_service "code.gitea.io/gitea/services/release"
@ -58,6 +59,13 @@ func GetReleaseByTag(ctx *context.APIContext) {
return
}
if release.IsDraft { // only the users with write access can see draft releases
if !ctx.IsSigned || !ctx.Repo.CanWrite(unit_model.TypeReleases) {
ctx.APIErrorNotFound()
return
}
}
if err = release.LoadAttributes(ctx); err != nil {
ctx.APIErrorInternal(err)
return

View File

@ -5,6 +5,7 @@ package common
import (
"fmt"
"log"
"net/http"
"strings"
@ -107,7 +108,11 @@ func ForwardedHeadersHandler(limit int, trustedProxies []string) func(h http.Han
return proxy.ForwardedHeaders(opt)
}
func Sessioner() (func(next http.Handler) http.Handler, error) {
func MustInitSessioner() func(next http.Handler) http.Handler {
// TODO: CHI-SESSION-GOB-REGISTER: chi-session has a design problem: it calls gob.Register for "Set"
// But if the server restarts, then the first "Get" will fail to decode the previously stored session data because the structs are not registered yet.
// So each package should make sure their structs are registered correctly during startup for session storage.
middleware, err := session.Sessioner(session.Options{
Provider: setting.SessionConfig.Provider,
ProviderConfig: setting.SessionConfig.ProviderConfig,
@ -120,8 +125,7 @@ func Sessioner() (func(next http.Handler) http.Handler, error) {
Domain: setting.SessionConfig.Domain,
})
if err != nil {
return nil, fmt.Errorf("failed to create session middleware: %w", err)
log.Fatalf("common.Sessioner failed: %v", err)
}
return middleware, nil
return middleware
}

View File

@ -55,8 +55,8 @@ func getSupportedDbTypeNames() (dbTypeNames []map[string]string) {
return dbTypeNames
}
// Contexter prepare for rendering installation page
func Contexter() func(next http.Handler) http.Handler {
// installContexter prepare for rendering installation page
func installContexter() func(next http.Handler) http.Handler {
rnd := templates.HTMLRenderer()
dbTypeNames := getSupportedDbTypeNames()
envConfigKeys := setting.CollectEnvConfigKeys()

View File

@ -8,7 +8,6 @@ import (
"html"
"net/http"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
@ -25,11 +24,8 @@ func Routes() *web.Router {
base.Methods("GET, HEAD", "/assets/*", public.FileHandlerFunc())
r := web.NewRouter()
if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
r.Use(sessionMid, Contexter())
} else {
log.Fatal("common.Sessioner failed: %v", err)
}
r.Use(common.MustInitSessioner(), installContexter())
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
r.Get("/post-install", InstallDone)

View File

@ -345,7 +345,7 @@ func EditUserPost(ctx *context.Context) {
}
if form.UserName != "" {
if err := user_service.RenameUser(ctx, u, form.UserName); err != nil {
if err := user_service.RenameUser(ctx, u, form.UserName, ctx.Doer); err != nil {
switch {
case user_model.IsErrUserIsNotLocal(err):
ctx.Data["Err_UserName"] = true
@ -409,7 +409,7 @@ func EditUserPost(ctx *context.Context) {
}
if form.Email != "" {
if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil {
if err := user_service.ReplacePrimaryEmailAddress(ctx, u, form.Email); err != nil {
switch {
case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true

View File

@ -277,8 +277,11 @@ type LinkAccountData struct {
GothUser goth.User
}
func init() {
gob.Register(LinkAccountData{}) // TODO: CHI-SESSION-GOB-REGISTER
}
func oauth2GetLinkAccountData(ctx *context.Context) *LinkAccountData {
gob.Register(LinkAccountData{})
v, ok := ctx.Session.Get("linkAccountData").(LinkAccountData)
if !ok {
return nil
@ -287,7 +290,6 @@ func oauth2GetLinkAccountData(ctx *context.Context) *LinkAccountData {
}
func Oauth2SetLinkAccountData(ctx *context.Context, linkAccountData LinkAccountData) error {
gob.Register(LinkAccountData{})
return updateSession(ctx, nil, map[string]any{
"linkAccountData": linkAccountData,
})

View File

@ -213,7 +213,7 @@ func SettingsRenamePost(ctx *context.Context) {
return
}
if err := user_service.RenameUser(ctx, ctx.Org.Organization.AsUser(), newOrgName); err != nil {
if err := user_service.RenameUser(ctx, ctx.Org.Organization.AsUser(), newOrgName, ctx.Doer); err != nil {
if user_model.IsErrUserAlreadyExist(err) {
ctx.JSONError(ctx.Tr("org.form.name_been_taken", newOrgName))
} else if db.IsErrNameReserved(err) {

View File

@ -10,7 +10,6 @@ import (
"net/url"
"path"
"strconv"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@ -42,8 +41,8 @@ type blameRow struct {
// RefBlame render blame page
func RefBlame(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
ctx.Data["IsBlame"] = true
prepareRepoViewContent(ctx, ctx.Repo.RefTypeNameSubURL())
// Get current entry user currently looking at.
if ctx.Repo.TreePath == "" {
@ -56,17 +55,6 @@ func RefBlame(ctx *context.Context) {
return
}
treeNames := strings.Split(ctx.Repo.TreePath, "/")
var paths []string
for i := range treeNames {
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
}
ctx.Data["Paths"] = paths
ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
blob := entry.Blob()
fileSize := blob.Size()
ctx.Data["FileSize"] = fileSize

View File

@ -41,7 +41,12 @@ const (
editorCommitChoiceNewBranch string = "commit-to-new-branch"
)
func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) *context.CommitFormOptions {
func prepareEditorPage(ctx *context.Context, editorAction string) *context.CommitFormOptions {
prepareHomeTreeSideBarSwitch(ctx)
return prepareEditorPageFormOptions(ctx, editorAction)
}
func prepareEditorPageFormOptions(ctx *context.Context, editorAction string) *context.CommitFormOptions {
cleanedTreePath := files_service.CleanGitTreePath(ctx.Repo.TreePath)
if cleanedTreePath != ctx.Repo.TreePath {
redirectTo := fmt.Sprintf("%s/%s/%s/%s", ctx.Repo.RepoLink, editorAction, util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(cleanedTreePath))
@ -283,7 +288,7 @@ func EditFile(ctx *context.Context) {
// on the "New File" page, we should add an empty path field to make end users could input a new name
prepareTreePathFieldsAndPaths(ctx, util.Iif(isNewFile, ctx.Repo.TreePath+"/", ctx.Repo.TreePath))
prepareEditorCommitFormOptions(ctx, editorAction)
prepareEditorPage(ctx, editorAction)
if ctx.Written() {
return
}
@ -376,15 +381,16 @@ func EditFilePost(ctx *context.Context) {
// DeleteFile render delete file page
func DeleteFile(ctx *context.Context) {
prepareEditorCommitFormOptions(ctx, "_delete")
prepareEditorPage(ctx, "_delete")
if ctx.Written() {
return
}
ctx.Data["PageIsDelete"] = true
prepareTreePathFieldsAndPaths(ctx, ctx.Repo.TreePath)
ctx.HTML(http.StatusOK, tplDeleteFile)
}
// DeleteFilePost response for deleting file
// DeleteFilePost response for deleting file or directory
func DeleteFilePost(ctx *context.Context) {
parsed := prepareEditorCommitSubmittedForm[*forms.DeleteRepoFileForm](ctx)
if ctx.Written() {
@ -392,17 +398,37 @@ func DeleteFilePost(ctx *context.Context) {
}
treePath := ctx.Repo.TreePath
_, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
if treePath == "" {
ctx.JSONError("cannot delete root directory") // it should not happen unless someone is trying to be malicious
return
}
// Check if the path is a directory
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath)
if err != nil {
ctx.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err)
return
}
var commitMessage string
if entry.IsDir() {
commitMessage = parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete_directory", treePath))
} else {
commitMessage = parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete", treePath))
}
_, err = files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
LastCommitID: parsed.form.LastCommit,
OldBranch: parsed.OldBranchName,
NewBranch: parsed.NewBranchName,
Files: []*files_service.ChangeRepoFile{
{
Operation: "delete",
TreePath: treePath,
Operation: "delete",
TreePath: treePath,
DeleteRecursively: true,
},
},
Message: parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete", treePath)),
Message: commitMessage,
Signoff: parsed.form.Signoff,
Author: parsed.GitCommitter,
Committer: parsed.GitCommitter,
@ -412,7 +438,11 @@ func DeleteFilePost(ctx *context.Context) {
return
}
ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", treePath))
if entry.IsDir() {
ctx.Flash.Success(ctx.Tr("repo.editor.directory_delete_success", treePath))
} else {
ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", treePath))
}
redirectTreePath := getClosestParentWithFiles(ctx.Repo.GitRepo, parsed.NewBranchName, treePath)
redirectForCommitChoice(ctx, parsed, redirectTreePath)
}
@ -420,7 +450,7 @@ func DeleteFilePost(ctx *context.Context) {
func UploadFile(ctx *context.Context) {
ctx.Data["PageIsUpload"] = true
prepareTreePathFieldsAndPaths(ctx, ctx.Repo.TreePath)
opts := prepareEditorCommitFormOptions(ctx, "_upload")
opts := prepareEditorPage(ctx, "_upload")
if ctx.Written() {
return
}

View File

@ -14,7 +14,7 @@ import (
)
func NewDiffPatch(ctx *context.Context) {
prepareEditorCommitFormOptions(ctx, "_diffpatch")
prepareEditorPage(ctx, "_diffpatch")
if ctx.Written() {
return
}

View File

@ -16,7 +16,7 @@ import (
)
func CherryPick(ctx *context.Context) {
prepareEditorCommitFormOptions(ctx, "_cherrypick")
prepareEditorPage(ctx, "_cherrypick")
if ctx.Written() {
return
}

View File

@ -1,24 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
)
const (
tplFindFiles templates.TplName = "repo/find/files"
)
// FindFiles render the page to find repository files
func FindFiles(ctx *context.Context) {
path := ctx.PathParam("*")
ctx.Data["TreeLink"] = ctx.Repo.RepoLink + "/src/" + util.PathEscapeSegments(path)
ctx.Data["DataLink"] = ctx.Repo.RepoLink + "/tree-list/" + util.PathEscapeSegments(path)
ctx.HTML(http.StatusOK, tplFindFiles)
}

View File

@ -146,7 +146,13 @@ func httpBase(ctx *context.Context) *serviceHandler {
// rely on the results of Contexter
if !ctx.IsSigned {
// TODO: support digit auth - which would be Authorization header with digit
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
if setting.OAuth2.Enabled {
// `Basic realm="Gitea"` tells the GCM to use builtin OAuth2 application: https://github.com/git-ecosystem/git-credential-manager/pull/1442
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
} else {
// If OAuth2 is disabled, then use another realm to avoid GCM OAuth2 attempt
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea (Basic Auth)"`)
}
ctx.HTTPError(http.StatusUnauthorized)
return nil
}

View File

@ -206,12 +206,11 @@ func SoftDeleteContentHistory(ctx *context.Context) {
ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if history.CommentID != commentID {
ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if commentID != 0 {
if history.CommentID != commentID {
ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if comment, err = issues_model.GetCommentByID(ctx, commentID); err != nil {
log.Error("can not get comment for issue content history %v. err=%v", historyID, err)
return

View File

@ -331,7 +331,7 @@ func UpdateViewedFiles(ctx *context.Context) {
updatedFiles[file] = state
}
if err := pull_model.UpdateReviewState(ctx, ctx.Doer.ID, pull.ID, data.HeadCommitSHA, updatedFiles); err != nil {
if _, err := pull_model.UpdateReviewState(ctx, ctx.Doer.ID, pull.ID, data.HeadCommitSHA, updatedFiles); err != nil {
ctx.ServerError("UpdateReview", err)
}
}

View File

@ -245,27 +245,17 @@ func LastCommit(ctx *context.Context) {
return
}
// The "/lastcommit/" endpoint is used to render the embedded HTML content for the directory file listing with latest commit info
// It needs to construct correct links to the file items, but the route only accepts a commit ID, not a full ref name (branch or tag).
// So we need to get the ref name from the query parameter "refSubUrl".
// TODO: LAST-COMMIT-ASYNC-LOADING: it needs more tests to cover this
refSubURL := path.Clean(ctx.FormString("refSubUrl"))
prepareRepoViewContent(ctx, util.IfZero(refSubURL, ctx.Repo.RefTypeNameSubURL()))
renderDirectoryFiles(ctx, 0)
if ctx.Written() {
return
}
var treeNames []string
paths := make([]string, 0, 5)
if len(ctx.Repo.TreePath) > 0 {
treeNames = strings.Split(ctx.Repo.TreePath, "/")
for i := range treeNames {
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
}
ctx.Data["HasParentPath"] = true
if len(paths)-2 >= 0 {
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
}
}
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["BranchLink"] = branchLink
ctx.HTML(http.StatusOK, tplRepoViewList)
}
@ -289,7 +279,9 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
return nil
}
ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
// TODO: LAST-COMMIT-ASYNC-LOADING: search this keyword to see more details
lastCommitLoaderURL := ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
ctx.Data["LastCommitLoaderURL"] = lastCommitLoaderURL + "?refSubUrl=" + url.QueryEscape(ctx.Repo.RefTypeNameSubURL())
// Get current entry user currently looking at.
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
@ -322,6 +314,21 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
ctx.ServerError("GetCommitsInfo", err)
return nil
}
{
if timeout != 0 && !setting.IsProd && !setting.IsInTesting {
log.Debug("first call to get directory file commit info")
clearFilesCommitInfo := func() {
log.Warn("clear directory file commit info to force async loading on frontend")
for i := range files {
files[i].Commit = nil
}
}
_ = clearFilesCommitInfo
// clearFilesCommitInfo() // TODO: LAST-COMMIT-ASYNC-LOADING: debug the frontend async latest commit info loading, uncomment this line, and it needs more tests
}
}
ctx.Data["Files"] = files
prepareDirectoryFileIcons(ctx, files)
for _, f := range files {
@ -334,16 +341,6 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
if !loadLatestCommitData(ctx, latestCommit) {
return nil
}
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treeLink := branchLink
if len(ctx.Repo.TreePath) > 0 {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
ctx.Data["TreeLink"] = treeLink
return allEntries
}

View File

@ -362,6 +362,32 @@ func redirectFollowSymlink(ctx *context.Context, treePathEntry *git.TreeEntry) b
return false
}
func prepareRepoViewContent(ctx *context.Context, refTypeNameSubURL string) {
// for: home, file list, file view, blame
ctx.Data["PageIsViewCode"] = true
ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled // show Upload File button or menu item
// prepare the tree path navigation
var treeNames, paths []string
branchLink := ctx.Repo.RepoLink + "/src/" + refTypeNameSubURL
treeLink := branchLink
if ctx.Repo.TreePath != "" {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
treeNames = strings.Split(ctx.Repo.TreePath, "/")
for i := range treeNames {
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
}
ctx.Data["HasParentPath"] = true
if len(paths)-2 >= 0 {
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
}
}
ctx.Data["Paths"] = paths
ctx.Data["TreeLink"] = treeLink
ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = branchLink
}
// Home render repository home page
func Home(ctx *context.Context) {
if handleRepoHomeFeed(ctx) {
@ -383,8 +409,7 @@ func Home(ctx *context.Context) {
title += ": " + ctx.Repo.Repository.Description
}
ctx.Data["Title"] = title
ctx.Data["PageIsViewCode"] = true
ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled // show New File / Upload File buttons
prepareRepoViewContent(ctx, ctx.Repo.RefTypeNameSubURL())
if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() {
// empty or broken repositories need to be handled differently
@ -405,26 +430,6 @@ func Home(ctx *context.Context) {
return
}
// prepare the tree path
var treeNames, paths []string
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treeLink := branchLink
if ctx.Repo.TreePath != "" {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
treeNames = strings.Split(ctx.Repo.TreePath, "/")
for i := range treeNames {
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
}
ctx.Data["HasParentPath"] = true
if len(paths)-2 >= 0 {
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
}
}
ctx.Data["Paths"] = paths
ctx.Data["TreeLink"] = treeLink
ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = branchLink
// some UI components are only shown when the tree path is root
isTreePathRoot := ctx.Repo.TreePath == ""
@ -455,7 +460,7 @@ func Home(ctx *context.Context) {
if isViewHomeOnlyContent(ctx) {
ctx.HTML(http.StatusOK, tplRepoViewContent)
} else if len(treeNames) != 0 {
} else if ctx.Repo.TreePath != "" {
ctx.HTML(http.StatusOK, tplRepoView)
} else {
ctx.HTML(http.StatusOK, tplRepoHome)

View File

@ -75,7 +75,7 @@ func ProfilePost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings")
return
}
if err := user_service.RenameUser(ctx, ctx.Doer, form.Name); err != nil {
if err := user_service.RenameUser(ctx, ctx.Doer, form.Name, ctx.Doer); err != nil {
switch {
case user_model.IsErrUserIsNotLocal(err):
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))

View File

@ -267,11 +267,7 @@ func Routes() *web.Router {
routes.Get("/ssh_info", misc.SSHInfo)
routes.Get("/api/healthz", healthcheck.Check)
if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
mid = append(mid, sessionMid, context.Contexter())
} else {
log.Fatal("common.Sessioner failed: %v", err)
}
mid = append(mid, common.MustInitSessioner(), context.Contexter())
// Get user from session if logged in.
mid = append(mid, webAuth(buildAuthGroup()))
@ -1188,7 +1184,6 @@ func registerWebRoutes(m *web.Router) {
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
m.Group("/{username}/{reponame}", func() {
m.Get("/find/*", repo.FindFiles)
m.Group("/tree-list", func() {
m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeList)
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)

View File

@ -5,6 +5,7 @@
package auth
import (
"errors"
"fmt"
"net/http"
"regexp"
@ -40,6 +41,20 @@ var globalVars = sync.OnceValue(func() *globalVarsStruct {
}
})
type ErrUserAuthMessage string
func (e ErrUserAuthMessage) Error() string {
return string(e)
}
func ErrAsUserAuthMessage(err error) (string, bool) {
var msg ErrUserAuthMessage
if errors.As(err, &msg) {
return msg.Error(), true
}
return "", false
}
// Init should be called exactly once when the application starts to allow plugins
// to allocate necessary resources
func Init() {

View File

@ -5,7 +5,6 @@
package auth
import (
"errors"
"net/http"
actions_model "code.gitea.io/gitea/models/actions"
@ -146,7 +145,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
return nil, err
}
if hasWebAuthn {
return nil, errors.New("basic authorization is not allowed while WebAuthn enrolled")
return nil, ErrUserAuthMessage("basic authorization is not allowed while WebAuthn enrolled")
}
if err := validateTOTP(req, u); err != nil {

View File

@ -22,9 +22,6 @@ import (
var gothRWMutex = sync.RWMutex{}
// UsersStoreKey is the key for the store
const UsersStoreKey = "gitea-oauth2-sessions"
// ProviderHeaderKey is the HTTP header key
const ProviderHeaderKey = "gitea-oauth2-provider"
@ -33,7 +30,7 @@ func Init(ctx context.Context) error {
// Lock our mutex
gothRWMutex.Lock()
gob.Register(&sessions.Session{})
gob.Register(&sessions.Session{}) // TODO: CHI-SESSION-GOB-REGISTER. FIXME: it seems to be an abuse, why the Session struct itself is stored in session store again?
gothic.Store = &SessionsStore{
maxLength: int64(setting.OAuth2.MaxTokenLength),

View File

@ -542,8 +542,9 @@ func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerifi
}
if verif.SigningUser != nil {
commitVerification.Signer = &api.PayloadUser{
Name: verif.SigningUser.Name,
Email: verif.SigningUser.Email,
UserName: verif.SigningUser.Name,
Name: verif.SigningUser.DisplayName(),
Email: verif.SigningEmail, // Use the email from the signature, not from the user profile
}
}
return commitVerification

View File

@ -1434,15 +1434,17 @@ outer:
}
}
// Explicitly store files that have changed in the database, if any is present at all.
// This has the benefit that the "Has Changed" attribute will be present as long as the user does not explicitly mark this file as viewed, so it will even survive a page reload after marking another file as viewed.
// On the other hand, this means that even if a commit reverting an unseen change is committed, the file will still be seen as changed.
if len(filesChangedSinceLastDiff) > 0 {
err := pull_model.UpdateReviewState(ctx, review.UserID, review.PullID, review.CommitSHA, filesChangedSinceLastDiff)
// Explicitly store files that have changed in the database, if any is present at all.
// This has the benefit that the "Has Changed" attribute will be present as long as the user does not explicitly mark this file as viewed, so it will even survive a page reload after marking another file as viewed.
// On the other hand, this means that even if a commit reverting an unseen change is committed, the file will still be seen as changed.
updatedReview, err := pull_model.UpdateReviewState(ctx, review.UserID, review.PullID, review.CommitSHA, filesChangedSinceLastDiff)
if err != nil {
log.Warn("Could not update review for user %d, pull %d, commit %s and the changed files %v: %v", review.UserID, review.PullID, review.CommitSHA, filesChangedSinceLastDiff, err)
return nil, err
}
// Update the local review to reflect the changes immediately
review = updatedReview
}
return review, nil

View File

@ -6,6 +6,7 @@ package incoming
import (
"context"
"crypto/tls"
"errors"
"fmt"
net_mail "net/mail"
"regexp"
@ -221,7 +222,7 @@ loop:
err := func() error {
r := msg.GetBody(section)
if r == nil {
return fmt.Errorf("could not get body from message: %w", err)
return errors.New("could not get body from message")
}
env, err := enmime.ReadEnvelope(r)

View File

@ -547,11 +547,15 @@ var escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p access_model.Permission, user *user_model.User) (bool, error) {
return isUserAllowedToMergeInRepoBranch(ctx, pr.BaseRepoID, pr.BaseBranch, p, user)
}
func isUserAllowedToMergeInRepoBranch(ctx context.Context, repoID int64, branch string, p access_model.Permission, user *user_model.User) (bool, error) {
if user == nil {
return false, nil
}
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repoID, branch)
if err != nil {
return false, err
}

View File

@ -71,7 +71,8 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
}
cmdCommit := gitcmd.NewCommand("commit").
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
AddOptionFormat("--message=%s", message)
AddOptionFormat("--message=%s", message).
AddArguments("--allow-empty")
if ctx.signKey == nil {
cmdCommit.AddArguments("--no-gpg-sign")
} else {

View File

@ -13,6 +13,7 @@ import (
"regexp"
"strings"
"time"
"unicode/utf8"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
@ -426,10 +427,16 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
for _, pr := range headBranchPRs {
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
if opts.NewCommitID != "" && opts.NewCommitID != objectFormat.EmptyObjectID().String() {
changed, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
changed, newMergeBase, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
if err != nil {
log.Error("checkIfPRContentChanged: %v", err)
}
if newMergeBase != "" && pr.MergeBase != newMergeBase {
pr.MergeBase = newMergeBase
if _, err := pr.UpdateColsIfNotMerged(ctx, "merge_base"); err != nil {
log.Error("Update merge base for %-v: %v", pr, err)
}
}
if changed {
// Mark old reviews as stale if diff to mergebase has changed
if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
@ -495,30 +502,30 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
// checkIfPRContentChanged checks if diff to target branch has changed by push
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, mergeBase string, err error) {
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr) // FIXME: why it still needs to create a temp repo, since the alongside calls like GetDiverging doesn't do so anymore
if err != nil {
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
return false, err
return false, "", err
}
defer cancel()
tmpRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
if err != nil {
return false, fmt.Errorf("OpenRepository: %w", err)
return false, "", fmt.Errorf("OpenRepository: %w", err)
}
defer tmpRepo.Close()
// Find the merge-base
_, base, err := tmpRepo.GetMergeBase("", "base", "tracking")
mergeBase, _, err = tmpRepo.GetMergeBase("", "base", "tracking")
if err != nil {
return false, fmt.Errorf("GetMergeBase: %w", err)
return false, "", fmt.Errorf("GetMergeBase: %w", err)
}
cmd := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
cmd := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, mergeBase)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
return false, mergeBase, fmt.Errorf("unable to open pipe for to run diff: %w", err)
}
stderr := new(bytes.Buffer)
@ -534,19 +541,19 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
}).
Run(ctx); err != nil {
if err == util.ErrNotEmpty {
return true, nil
return true, mergeBase, nil
}
err = gitcmd.ConcatenateError(err, stderr.String())
log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v",
newCommitID, oldCommitID, base,
newCommitID, oldCommitID, mergeBase,
pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch,
err)
return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
return false, mergeBase, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, mergeBase, err)
}
return false, nil
return false, mergeBase, nil
}
// PushToBaseRepo pushes commits from branches of head repository to
@ -838,51 +845,53 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
stringBuilder := strings.Builder{}
if !setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages {
// use PR's title and description as squash commit message
message := strings.TrimSpace(pr.Issue.Content)
stringBuilder.WriteString(message)
if stringBuilder.Len() > 0 {
stringBuilder.WriteRune('\n')
if !commitMessageTrailersPattern.MatchString(message) {
// TODO: this trailer check doesn't work with the separator line added below for the co-authors
stringBuilder.WriteRune('\n')
}
}
} else {
// use PR's commit messages as squash commit message
// commits list is in reverse chronological order
maxMsgSize := setting.Repository.PullRequest.DefaultMergeMessageSize
for i := len(commits) - 1; i >= 0; i-- {
commit := commits[i]
msg := strings.TrimSpace(commit.CommitMessage)
if msg == "" {
continue
}
// This format follows GitHub's squash commit message style,
// even if there are other "* " in the commit message body, they are written as-is.
// Maybe, ideally, we should indent those lines too.
_, _ = fmt.Fprintf(&stringBuilder, "* %s\n\n", msg)
if maxMsgSize > 0 && stringBuilder.Len() >= maxMsgSize {
tmp := stringBuilder.String()
wasValidUtf8 := utf8.ValidString(tmp)
tmp = tmp[:maxMsgSize] + "..."
if wasValidUtf8 {
// If the message was valid UTF-8 before truncation, ensure it remains valid after truncation
// For non-utf8 messages, we can't do much about it, end users should use utf-8 as much as possible
tmp = strings.ToValidUTF8(tmp, "")
}
stringBuilder.Reset()
stringBuilder.WriteString(tmp)
break
}
}
}
// commits list is in reverse chronological order
first := true
for i := len(commits) - 1; i >= 0; i-- {
commit := commits[i]
if setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages {
maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize
if maxSize < 0 || stringBuilder.Len() < maxSize {
var toWrite []byte
if first {
first = false
toWrite = []byte(strings.TrimPrefix(commit.CommitMessage, pr.Issue.Title))
} else {
toWrite = []byte(commit.CommitMessage)
}
if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 {
toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...)
}
if _, err := stringBuilder.Write(toWrite); err != nil {
log.Error("Unable to write commit message Error: %v", err)
return ""
}
if _, err := stringBuilder.WriteRune('\n'); err != nil {
log.Error("Unable to write commit message Error: %v", err)
return ""
}
}
}
// collect co-authors
for _, commit := range commits {
authorString := commit.Author.String()
if uniqueAuthors.Add(authorString) && authorString != posterSig {
// Compare use account as well to avoid adding the same author multiple times
// times when email addresses are private or multiple emails are used.
// when email addresses are private or multiple emails are used.
commitUser, _ := user_model.GetUserByEmail(ctx, commit.Author.Email)
if commitUser == nil || commitUser.ID != pr.Issue.Poster.ID {
authors = append(authors, authorString)
@ -890,12 +899,12 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
}
}
// Consider collecting the remaining authors
// collect the remaining authors
if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors {
skip := limit
limit = 30
for {
commits, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip)
commits, err = gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip)
if err != nil {
log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
return ""
@ -916,19 +925,15 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
}
}
if stringBuilder.Len() > 0 && len(authors) > 0 {
// TODO: this separator line doesn't work with the trailer check (commitMessageTrailersPattern) above
stringBuilder.WriteString("---------\n\n")
}
for _, author := range authors {
if _, err := stringBuilder.WriteString("Co-authored-by: "); err != nil {
log.Error("Unable to write to string builder Error: %v", err)
return ""
}
if _, err := stringBuilder.WriteString(author); err != nil {
log.Error("Unable to write to string builder Error: %v", err)
return ""
}
if _, err := stringBuilder.WriteRune('\n'); err != nil {
log.Error("Unable to write to string builder Error: %v", err)
return ""
}
stringBuilder.WriteString("Co-authored-by: ")
stringBuilder.WriteString(author)
stringBuilder.WriteRune('\n')
}
return stringBuilder.String()

View File

@ -333,7 +333,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
if headCommitID == commitID {
stale = false
} else {
stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
stale, _, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
if err != nil {
return nil, nil, err
}

View File

@ -101,11 +101,11 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
}
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
// update PR means send new commits to PR head branch from base branch
func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (mergeAllowed, rebaseAllowed bool, err error) {
if pull.Flow == issues_model.PullRequestFlowAGit {
return false, false, nil
}
if user == nil {
return false, false, nil
}
@ -121,54 +121,46 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
return false, false, err
}
pr := &issues_model.PullRequest{
HeadRepoID: pull.BaseRepoID,
HeadRepo: pull.BaseRepo,
BaseRepoID: pull.HeadRepoID,
BaseRepo: pull.HeadRepo,
HeadBranch: pull.BaseBranch,
BaseBranch: pull.HeadBranch,
}
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
if err != nil {
return false, false, err
}
if err := pr.LoadBaseRepo(ctx); err != nil {
return false, false, err
}
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if err != nil {
// 1. check base repository's AllowRebaseUpdate configuration
// it is a config in base repo but controls the head (fork) repo's "Update" behavior
{
prBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if repo_model.IsErrUnitTypeNotExist(err) {
return false, false, nil
return false, false, nil // the PR unit is disabled in base repo
} else if err != nil {
return false, false, fmt.Errorf("get base repo unit: %v", err)
}
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
return false, false, err
rebaseAllowed = prBaseUnit.PullRequestsConfig().AllowRebaseUpdate
}
rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
if pb != nil {
pb.Repo = pull.BaseRepo
if !pb.CanUserForcePush(ctx, user) {
rebaseAllowed = false
// 2. check head branch protection whether rebase is allowed, if pb not found then rebase depends on the above setting
{
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.HeadRepoID, pull.HeadBranch)
if err != nil {
return false, false, err
}
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
if pb != nil {
pb.Repo = pull.HeadRepo
rebaseAllowed = rebaseAllowed && pb.CanUserForcePush(ctx, user)
}
}
// 3. check whether user has write access to head branch
baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.BaseRepo, user)
if err != nil {
return false, false, err
}
mergeAllowed, err = IsUserAllowedToMerge(ctx, pr, headRepoPerm, user)
mergeAllowed, err = isUserAllowedToMergeInRepoBranch(ctx, pull.HeadRepoID, pull.HeadBranch, headRepoPerm, user)
if err != nil {
return false, false, err
}
// 4. if the pull creator allows maintainer to edit, it means the write permissions of the head branch has been
// granted to the user with write permission of the base repository
if pull.AllowMaintainerEdit {
mergeAllowedMaintainer, err := IsUserAllowedToMerge(ctx, pr, baseRepoPerm, user)
mergeAllowedMaintainer, err := isUserAllowedToMergeInRepoBranch(ctx, pull.BaseRepoID, pull.BaseBranch, baseRepoPerm, user)
if err != nil {
return false, false, err
}
@ -176,6 +168,9 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
mergeAllowed = mergeAllowed || mergeAllowedMaintainer
}
// if merge is not allowed, rebase is also not allowed
rebaseAllowed = rebaseAllowed && mergeAllowed
return mergeAllowed, rebaseAllowed, nil
}

View File

@ -361,7 +361,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
if err != nil {
return fmt.Errorf("GetProtectedTags: %w", err)
}
isAllowed, err := git_model.IsUserAllowedToControlTag(ctx, protectedTags, rel.TagName, rel.PublisherID)
isAllowed, err := git_model.IsUserAllowedToControlTag(ctx, protectedTags, rel.TagName, doer.ID)
if err != nil {
return err
}

View File

@ -135,6 +135,14 @@ func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...st
return fileList, nil
}
func (t *TemporaryUploadRepository) RemoveRecursivelyFromIndex(ctx context.Context, path string) error {
_, _, err := gitcmd.NewCommand("rm", "--cached", "-r").
AddDynamicArguments(path).
WithDir(t.basePath).
RunStdBytes(ctx)
return err
}
// RemoveFilesFromIndex removes the given files from the index
func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, filenames ...string) error {
objFmt, err := t.gitRepo.GetObjectFormat()

View File

@ -46,7 +46,10 @@ type ChangeRepoFile struct {
FromTreePath string
ContentReader io.ReadSeeker
SHA string
Options *RepoFileOptions
DeleteRecursively bool // when deleting, work as `git rm -r ...`
Options *RepoFileOptions // FIXME: need to refactor, internal usage only
}
// ChangeRepoFilesOptions holds the repository files update options
@ -69,26 +72,6 @@ type RepoFileOptions struct {
executable bool
}
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
type ErrRepoFileDoesNotExist struct {
Path string
Name string
}
// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist.
func IsErrRepoFileDoesNotExist(err error) bool {
_, ok := err.(ErrRepoFileDoesNotExist)
return ok
}
func (err ErrRepoFileDoesNotExist) Error() string {
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
}
func (err ErrRepoFileDoesNotExist) Unwrap() error {
return util.ErrNotExist
}
type LazyReadSeeker interface {
io.ReadSeeker
io.Closer
@ -217,24 +200,6 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
}
for _, file := range opts.Files {
if file.Operation == "delete" {
// Get the files in the index
filesInIndex, err := t.LsFiles(ctx, file.TreePath)
if err != nil {
return nil, fmt.Errorf("DeleteRepoFile: %w", err)
}
// Find the file we want to delete in the index
inFilelist := slices.Contains(filesInIndex, file.TreePath)
if !inFilelist {
return nil, ErrRepoFileDoesNotExist{
Path: file.TreePath,
}
}
}
}
if hasOldBranch {
// Get the commit of the original branch
commit, err := t.GetBranchCommit(opts.OldBranch)
@ -272,8 +237,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
addedLfsPointers = append(addedLfsPointers, *addedLfsPointer)
}
case "delete":
if err = t.RemoveFilesFromIndex(ctx, file.TreePath); err != nil {
return nil, err
if file.DeleteRecursively {
if err = t.RemoveRecursivelyFromIndex(ctx, file.TreePath); err != nil {
return nil, err
}
} else {
if err = t.RemoveFilesFromIndex(ctx, file.TreePath); err != nil {
return nil, err
}
}
default:
return nil, fmt.Errorf("invalid file operation: %s %s, supported operations are create, update, delete", file.Operation, file.Options.treePath)

View File

@ -77,43 +77,44 @@ func ReplacePrimaryEmailAddress(ctx context.Context, u *user_model.User, emailSt
return err
}
if !u.IsOrganization() {
// Check if address exists already
email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
if err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
if email != nil {
if email.IsPrimary && email.UID == u.ID {
return nil
return db.WithTx(ctx, func(ctx context.Context) error {
if !u.IsOrganization() {
// Check if address exists already
email, err := user_model.GetEmailAddressByEmail(ctx, emailStr)
if err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
if email != nil {
if email.IsPrimary && email.UID == u.ID {
return nil
}
return user_model.ErrEmailAlreadyUsed{Email: emailStr}
}
// Remove old primary address
primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
if err != nil {
return err
}
if _, err := db.DeleteByID[user_model.EmailAddress](ctx, primary.ID); err != nil {
return err
}
// Insert new primary address
if _, err := user_model.InsertEmailAddress(ctx, &user_model.EmailAddress{
UID: u.ID,
Email: emailStr,
IsActivated: true,
IsPrimary: true,
}); err != nil {
return err
}
return user_model.ErrEmailAlreadyUsed{Email: emailStr}
}
// Remove old primary address
primary, err := user_model.GetPrimaryEmailAddressOfUser(ctx, u.ID)
if err != nil {
return err
}
if _, err := db.DeleteByID[user_model.EmailAddress](ctx, primary.ID); err != nil {
return err
}
u.Email = emailStr
// Insert new primary address
email = &user_model.EmailAddress{
UID: u.ID,
Email: emailStr,
IsActivated: true,
IsPrimary: true,
}
if _, err := user_model.InsertEmailAddress(ctx, email); err != nil {
return err
}
}
u.Email = emailStr
return user_model.UpdateUserCols(ctx, u, "email")
return user_model.UpdateUserCols(ctx, u, "email")
})
}
func AddEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error {

View File

@ -31,17 +31,15 @@ import (
)
// RenameUser renames a user
func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error {
func RenameUser(ctx context.Context, u *user_model.User, newUserName string, doer *user_model.User) error {
if newUserName == u.Name {
return nil
}
// Non-local users are not allowed to change their username.
if !u.IsOrganization() && !u.IsLocal() {
return user_model.ErrUserIsNotLocal{
UID: u.ID,
Name: u.Name,
}
// Non-local users are not allowed to change their own username, but admins are
isExternalUser := !u.IsOrganization() && !u.IsLocal()
if isExternalUser && !doer.IsAdmin {
return user_model.ErrUserIsNotLocal{UID: u.ID, Name: u.Name}
}
if err := user_model.IsUsableUsername(newUserName); err != nil {

View File

@ -20,6 +20,7 @@ import (
org_service "code.gitea.io/gitea/services/org"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
@ -101,23 +102,31 @@ func TestRenameUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21})
t.Run("Non-Local", func(t *testing.T) {
u := &user_model.User{
Type: user_model.UserTypeIndividual,
LoginType: auth.OAuth2,
t.Run("External user", func(t *testing.T) {
adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, IsAdmin: true})
externalUser := &user_model.User{
Name: "external_user",
Email: "external_user@gitea.io",
LoginType: auth.LDAP,
}
assert.ErrorIs(t, RenameUser(t.Context(), u, "user_rename"), user_model.ErrUserIsNotLocal{})
require.NoError(t, user_model.CreateUser(t.Context(), externalUser, &user_model.Meta{}))
err := RenameUser(t.Context(), externalUser, externalUser.Name+"_changed", externalUser)
assert.True(t, user_model.IsErrUserIsNotLocal(err), "external user is not allowed to rename themselves")
err = RenameUser(t.Context(), externalUser, externalUser.Name+"_changed", adminUser)
assert.NoError(t, err, "admin can rename external user")
})
t.Run("Same username", func(t *testing.T) {
assert.NoError(t, RenameUser(t.Context(), user, user.Name))
assert.NoError(t, RenameUser(t.Context(), user, user.Name, user))
})
t.Run("Non usable username", func(t *testing.T) {
usernames := []string{"--diff", ".well-known", "gitea-actions", "aaa.atom", "aa.png"}
for _, username := range usernames {
assert.Error(t, user_model.IsUsableUsername(username), "non-usable username: %s", username)
assert.Error(t, RenameUser(t.Context(), user, username), "non-usable username: %s", username)
assert.Error(t, RenameUser(t.Context(), user, username, user), "non-usable username: %s", username)
}
})
@ -126,7 +135,7 @@ func TestRenameUser(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{ID: user.ID, Name: caps})
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name})
assert.NoError(t, RenameUser(t.Context(), user, caps))
assert.NoError(t, RenameUser(t.Context(), user, caps, user))
unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID, Name: caps})
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: caps})
@ -135,17 +144,17 @@ func TestRenameUser(t *testing.T) {
t.Run("Already exists", func(t *testing.T) {
existUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.ErrorIs(t, RenameUser(t.Context(), user, existUser.Name), user_model.ErrUserAlreadyExist{Name: existUser.Name})
assert.ErrorIs(t, RenameUser(t.Context(), user, existUser.LowerName), user_model.ErrUserAlreadyExist{Name: existUser.LowerName})
assert.ErrorIs(t, RenameUser(t.Context(), user, existUser.Name, user), user_model.ErrUserAlreadyExist{Name: existUser.Name})
assert.ErrorIs(t, RenameUser(t.Context(), user, existUser.LowerName, user), user_model.ErrUserAlreadyExist{Name: existUser.LowerName})
newUsername := fmt.Sprintf("uSEr%d", existUser.ID)
assert.ErrorIs(t, RenameUser(t.Context(), user, newUsername), user_model.ErrUserAlreadyExist{Name: newUsername})
assert.ErrorIs(t, RenameUser(t.Context(), user, newUsername, user), user_model.ErrUserAlreadyExist{Name: newUsername})
})
t.Run("Normal", func(t *testing.T) {
oldUsername := user.Name
newUsername := "User_Rename"
assert.NoError(t, RenameUser(t.Context(), user, newUsername))
assert.NoError(t, RenameUser(t.Context(), user, newUsername, user))
unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID, Name: newUsername, LowerName: strings.ToLower(newUsername)})
redirectUID, err := user_model.LookupUserRedirect(t.Context(), oldUsername)

View File

@ -135,7 +135,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
if hasDefaultBranch {
if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil {
log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err)
return fmt.Errorf("fnable to read HEAD tree to index in: %s %w", basePath, err)
return fmt.Errorf("unable to read HEAD tree to index in: %s %w", basePath, err)
}
}

View File

@ -9,7 +9,7 @@
{{.CsrfTokenHtml}}
<div class="field {{if .Err_UserName}}error{{end}}">
<label for="user_name">{{ctx.Locale.Tr "username"}}</label>
<input id="user_name" name="user_name" value="{{.User.Name}}" {{if not .User.IsLocal}}disabled{{end}} maxlength="40">
<input id="user_name" name="user_name" value="{{.User.Name}}" maxlength="40">
</div>
<!-- Types and name -->
<div class="inline required field {{if .Err_LoginType}}error{{end}}">

View File

@ -120,6 +120,13 @@
{{svg "octicon-question"}}
{{ctx.Locale.Tr "help"}}
</a>
{{if .IsAdmin}}
<div class="divider"></div>
<a class="{{if .PageIsAdmin}}active {{end}}item" href="{{AppSubUrl}}/-/admin">
{{svg "octicon-server"}}
{{ctx.Locale.Tr "admin_panel"}}
</a>
{{end}}
<div class="divider"></div>
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
{{svg "octicon-sign-out"}}

View File

@ -78,7 +78,9 @@
<div class="ui circular label project-column-issue-count">
{{.NumIssues}}
</div>
<div class="project-column-title-text gt-ellipsis">{{.Title}}</div>
<div class="project-column-title-text flex-text-inline gt-ellipsis" {{if .Default}}data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.default_column_hint"}}"{{end}}>
{{if .Default}}{{svg "octicon-star"}} {{end}}{{.Title}}
</div>
{{if $canWriteProject}}
<div class="ui dropdown tw-p-1">
{{svg "octicon-kebab-horizontal"}}
@ -98,7 +100,7 @@
data-modal-confirm-header="{{ctx.Locale.Tr "repo.projects.column.set_default"}}"
data-modal-confirm-content="{{ctx.Locale.Tr "repo.projects.column.set_default_desc"}}"
>
{{svg "octicon-pin"}} {{ctx.Locale.Tr "repo.projects.column.set_default"}}
{{svg "octicon-star"}} {{ctx.Locale.Tr "repo.projects.column.set_default"}}
</a>
<a class="item button link-action" data-url="{{$.Link}}/{{.ID}}" data-link-action-method="DELETE"
data-modal-confirm-header="{{ctx.Locale.Tr "repo.projects.column.delete"}}"

View File

@ -1,13 +1,30 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository file editor delete">
{{template "repo/header" .}}
<div class="ui container">
<div class="ui container fluid padded">
{{template "base/alert" .}}
<form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
{{.CsrfTokenHtml}}
{{template "repo/editor/common_top" .}}
{{template "repo/editor/commit_form" .}}
</form>
<div class="repo-view-container">
{{template "repo/view_file_tree" .}}
<div class="repo-view-content">
<form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
{{.CsrfTokenHtml}}
{{template "repo/editor/common_top" .}}
<div class="repo-editor-header">
{{/* although the UI isn't good enough, this header is necessary for the "left file tree view" toggle button, this button must exist */}}
{{template "repo/view_file_tree_toggle_button" .}}
{{/* then, to make the page looks overall good, add the breadcrumb here to make the toggle button can be shown in a text row, but not a single button*/}}
<div class="breadcrumb">
<a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
{{range $i, $v := .TreeNames}}
<div class="breadcrumb-divider">/</div>
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
{{end}}
</div>
</div>
{{template "repo/editor/commit_form" .}}
</form>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

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