Merge remote-tracking branch 'upstream/main' into limit-repo-size

This commit is contained in:
DmitryFrolovTri
2023-05-17 05:41:11 +00:00
202 changed files with 4516 additions and 3104 deletions
+23 -3
View File
@@ -9,13 +9,15 @@ parserOptions:
ecmaVersion: latest
plugins:
- eslint-plugin-unicorn
- "@eslint-community/eslint-plugin-eslint-comments"
- eslint-plugin-custom-elements
- eslint-plugin-import
- eslint-plugin-jquery
- eslint-plugin-no-jquery
- eslint-plugin-sonarjs
- eslint-plugin-custom-elements
- eslint-plugin-regexp
- eslint-plugin-sonarjs
- eslint-plugin-unicorn
- eslint-plugin-wc
env:
es2022: true
@@ -43,6 +45,15 @@ overrides:
import/no-unused-modules: [0]
rules:
"@eslint-community/eslint-comments/disable-enable-pair": [2]
"@eslint-community/eslint-comments/no-aggregating-enable": [2]
"@eslint-community/eslint-comments/no-duplicate-disable": [2]
"@eslint-community/eslint-comments/no-restricted-disable": [0]
"@eslint-community/eslint-comments/no-unlimited-disable": [2]
"@eslint-community/eslint-comments/no-unused-disable": [2]
"@eslint-community/eslint-comments/no-unused-enable": [2]
"@eslint-community/eslint-comments/no-use": [0]
"@eslint-community/eslint-comments/require-description": [0]
accessor-pairs: [2]
array-bracket-newline: [0]
array-bracket-spacing: [2, never]
@@ -717,6 +728,15 @@ rules:
use-isnan: [2]
valid-typeof: [2, {requireStringLiterals: true}]
vars-on-top: [0]
wc/attach-shadow-constructor: [2]
wc/guard-super-call: [2]
wc/no-closed-shadow-root: [2]
wc/no-constructor-attributes: [2]
wc/no-constructor-params: [2]
wc/no-invalid-element-name: [0] # covered by custom-elements/valid-tag-name
wc/no-self-class: [2]
wc/no-typos: [2]
wc/require-listener-teardown: [2]
wrap-iife: [2, inside]
wrap-regex: [0]
yield-star-spacing: [2, after]
+8 -9
View File
@@ -1,21 +1,20 @@
name: "Cron: Update licenses and gitignores"
name: cron-licenses
on:
schedule:
# weekly on Monday at 0:07 UTC
- cron: "7 0 * * 1"
- cron: "7 0 * * 1" # every Monday at 00:07 UTC
jobs:
cron:
cron-licenses:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '>=1.20.1'
- name: update licenses and gitignores
run: timeout -s ABRT 40m make generate-license generate-gitignore
go-version: ">=1.20.1"
- run: make generate-license generate-gitignore
timeout-minutes: 40
- name: push translations to repo
uses: appleboy/git-push-action@v0.0.2
with:
@@ -1,8 +1,8 @@
name: 'Lock Threads'
name: cron-lock
on:
schedule:
- cron: '0 0 * * *' # Run once a day
- cron: "0 0 * * *" # every day at 00:00 UTC
workflow_dispatch:
permissions:
@@ -15,6 +15,7 @@ concurrency:
jobs:
action:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
steps:
- uses: dessant/lock-threads@v4
with:
+9 -8
View File
@@ -1,15 +1,15 @@
name: "Cron: Pull translations from Crowdin"
name: cron-translations
on:
schedule:
- cron: "7 0 * * *" # every day at 0:07 UTC
- cron: "7 0 * * *" # every day at 00:07 UTC
jobs:
crowdin_pull:
crowdin-pull:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: download from crowdin
uses: docker://jonasfranz/crowdin
env:
@@ -30,16 +30,17 @@ jobs:
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "git@github.com:go-gitea/gitea.git"
ssh_key: ${{ secrets.DEPLOY_KEY }}
crowdin_push:
crowdin-push:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: push translations to crowdin
uses: docker://jonasfranz/crowdin
env:
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
PLUGIN_UPLOAD: true
PLUGIN_EXPORT_DIR: options/locale/
PLUGIN_IGNORE_BRANCH: true
PLUGIN_PROJECT_IDENTIFIER: gitea
PLUGIN_FILES: |
@@ -1,4 +1,4 @@
name: "Docs: Publish"
name: publish-docs
on:
push:
@@ -11,12 +11,10 @@ jobs:
compliance-docs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.1'
go-version: ">=1.20.1"
- name: build docs
run: |
cd docs
@@ -0,0 +1,22 @@
name: compliance-docs
on:
pull_request:
paths:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
compliance-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: make deps-frontend
- run: make lint-md
@@ -0,0 +1,13 @@
name: compliance
on:
pull_request:
paths:
- "docs/**"
- "*.md"
jobs:
compliance-docs:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
+43 -82
View File
@@ -1,142 +1,103 @@
name: "Pull: Compliance Tests"
name: compliance
on: [pull_request]
on:
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint_basic:
lint-backend:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
go-version: ">=1.20"
check-latest: true
- name: deps-backend
run: make deps-backend deps-tools
- name: lint backend
run: make lint-backend
- run: make deps-backend deps-tools
- run: make lint-backend
env:
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify
lint_windows:
lint-go-windows:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
go-version: ">=1.20"
check-latest: true
- name: deps-backend
run: make deps-backend deps-tools
- name: lint-backend-windows
run: make lint-go-windows lint-go-vet
- run: make deps-backend deps-tools
- run: make lint-go-windows lint-go-vet
env:
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows
GOARCH: amd64
lint_gogit:
lint-go-gogit:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
go-version: ">=1.20"
check-latest: true
- name: deps-backend
run: make deps-backend deps-tools
- name: lint-backend-gogit
run: make lint-backend
- run: make deps-backend deps-tools
- run: make lint-go
env:
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org
TAGS: bindata gogit sqlite sqlite_unlock_notify
- name: checks backend
run: make --always-make checks-backend # ensure the 'go-licenses' make target runs
check_backend:
checks-backend:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
go-version: ">=1.20"
check-latest: true
- name: deps-backend
run: make deps-backend deps-tools
- name: checks backend
run: make --always-make checks-backend # ensure the 'go-licenses' make target runs
- run: make deps-backend deps-tools
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
frontend:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v3
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- name: deps-frontend
run: make deps-frontend
- name: lint frontend
run: make lint-frontend
- name: checks frontend
run: make checks-frontend
- name: test frontend
run: make test-frontend
- run: make deps-frontend
- run: make lint-frontend
- run: make checks-frontend
backend:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
go-version: ">=1.20"
check-latest: true
- name: setup node
uses: actions/setup-node@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- name: deps-backend
run: make deps-backend deps-tools
- name: deps-frontend
run: make deps-frontend
- name: build frontend
run: make frontend
- name: build-backend-no-gcc
run: go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
env:
GOPROXY: https://goproxy.io
- run: make deps-backend deps-tools
- run: make deps-frontend
- run: make frontend
- run: go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
- name: build-backend-arm64
run: make backend # test cross compile
env:
GOPROXY: https://goproxy.io
GOOS: linux
GOARCH: arm64
TAGS: bindata gogit
- name: build-backend-windows
run: go build -o gitea_windows
env:
GOPROXY: https://goproxy.io
GOOS: windows
GOARCH: amd64
TAGS: bindata gogit
- name: build-backend-386
run: go build -o gitea_linux_386 # test if compatible with 32 bit
env:
GOPROXY: https://goproxy.io
GOOS: linux
GOARCH: 386
@@ -1,26 +0,0 @@
name: "Pull: Compliance testing for documentation"
on:
pull_request:
paths:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
compliance-docs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 20
- name: install dependencies
run: make deps-frontend
- name: lint markdown
run: make lint-md
@@ -0,0 +1,38 @@
name: db-tests
on:
pull_request:
paths:
- "docs/**"
- "*.md"
jobs:
test-pgsql:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
test-sqlite:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
test-unit:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
test-mysql5:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
test-mysql8:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
test-mssql:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
@@ -1,14 +1,17 @@
name: "Pull: Database Tests"
name: db-tests
on: [pull_request]
on:
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
# PostgreSQL Tests
db_pgsql_test:
test-pgsql:
runs-on: ubuntu-latest
services:
pgsql:
@@ -33,61 +36,45 @@ jobs:
ports:
- "9000:9000"
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
go-version: ">=1.20.0"
- name: Add hosts to /etc/hosts
run: echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata
- name: run tests
run: timeout -s ABRT 50m make test-pgsql-migration test-pgsql
- run: make test-pgsql-migration test-pgsql
timeout-minutes: 50
env:
GOPROXY: https://goproxy.io
TAGS: bindata gogit
RACE_ENABLED: true
TEST_TAGS: gogit
TEST_LDAP: 1
USE_REPO_TEST_DIR: 1
# SQLite Tests
db_sqlite_test:
test-sqlite:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
go-version: ">=1.20.0"
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata gogit sqlite sqlite_unlock_notify
- name: run tests
run: timeout -s ABRT 50m make test-sqlite-migration test-sqlite
- run: make test-sqlite-migration test-sqlite
timeout-minutes: 50
env:
GOPROXY: https://goproxy.io
TAGS: bindata gogit sqlite sqlite_unlock_notify
RACE_ENABLED: true
TEST_TAGS: gogit sqlite sqlite_unlock_notify
USE_REPO_TEST_DIR: 1
# Unit Tests
db_unit_tests:
test-unit:
runs-on: ubuntu-latest
services:
mysql:
@@ -112,48 +99,45 @@ jobs:
- "993:993"
redis:
image: redis
# Set health checks to wait until redis has started
options: >-
options: >- # wait until redis has started
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10
ports:
- 6379:6379
minio:
image: bitnami/minio:2021.3.17
env:
MINIO_ACCESS_KEY: 123456
MINIO_SECRET_KEY: 12345678
ports:
- "9000:9000"
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
go-version: ">=1.20.0"
- name: Add hosts to /etc/hosts
run: echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata
- name: unit tests
- name: unit-tests
run: make unit-test-coverage test-check
env:
GOPROXY: https://goproxy.io
TAGS: bindata
RACE_ENABLED: true
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
- name: unit tests (gogit)
- name: unit-tests-gogit
run: make unit-test-coverage test-check
env:
GOPROXY: https://goproxy.io
TAGS: bindata gogit
RACE_ENABLED: true
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
# MySQL Tests
db_mysql_test:
test-mysql5:
runs-on: ubuntu-latest
services:
mysql:
@@ -177,33 +161,25 @@ jobs:
- "587:587"
- "993:993"
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
go-version: ">=1.20.0"
- name: Add hosts to /etc/hosts
run: echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata
- name: run tests
run: make test-mysql-migration integration-test-coverage
env:
GOPROXY: https://goproxy.io
TAGS: bindata
RACE_ENABLED: true
USE_REPO_TEST_DIR: 1
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
# MySQL8 Tests
db_mysql8_test:
test-mysql8:
runs-on: ubuntu-latest
services:
mysql8:
@@ -214,31 +190,23 @@ jobs:
ports:
- "3306:3306"
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
go-version: ">=1.20.0"
- name: Add hosts to /etc/hosts
run: echo "127.0.0.1 mysql8" | sudo tee -a /etc/hosts
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata
- name: run tests
run: timeout -s ABRT 50m make test-mysql8-migration test-mysql8
- run: make test-mysql8-migration test-mysql8
timeout-minutes: 50
env:
GOPROXY: https://goproxy.io
TAGS: bindata
USE_REPO_TEST_DIR: 1
# MSSQL Tests
db_mssql_test:
test-mssql:
runs-on: ubuntu-latest
services:
mssql:
@@ -250,25 +218,18 @@ jobs:
ports:
- "1433:1433"
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.0'
go-version: ">=1.20.0"
- name: Add hosts to /etc/hosts
run: echo "127.0.0.1 mssql" | sudo tee -a /etc/hosts
- name: install dependencies
run: make deps-backend
- name: build
run: make backend
- run: make deps-backend
- run: make backend
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
TAGS: bindata
- name: run tests
run: timeout -s ABRT 50m make test-mssql-migration test-mssql
- run: make test-mssql-migration test-mssql
timeout-minutes: 50
env:
GOPROXY: https://goproxy.io
TAGS: bindata
USE_REPO_TEST_DIR: 1
@@ -0,0 +1,13 @@
name: docker-dryrun
on:
pull_request:
paths:
- "docs/**"
- "*.md"
jobs:
docker-dryrun:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
+21
View File
@@ -0,0 +1,21 @@
name: docker-dryrun
on:
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
docker-dryrun:
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@v2
- uses: docker/build-push-action@v4
with:
push: false
tags: gitea/gitea:linux-amd64
-23
View File
@@ -1,23 +0,0 @@
name: "Pull: Docker Dry Run"
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
docker_dryrun:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v4
with:
push: false
tags: gitea/gitea:linux-amd64
build-args: |
GOPROXY=https://goproxy.io
@@ -0,0 +1,13 @@
name: e2e-tests
on:
pull_request:
paths:
- "docs/**"
- "*.md"
jobs:
test-e2e:
runs-on: ubuntu-latest
steps:
- run: echo "No build required"
+30
View File
@@ -0,0 +1,30 @@
name: e2e-tests
on:
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ">=1.20"
check-latest: true
- uses: actions/setup-node@v3
with:
node-version: 20
- run: make deps-frontend frontend deps-backend
- run: npx playwright install --with-deps
- run: make test-e2e-sqlite
timeout-minutes: 40
env:
USE_REPO_TEST_DIR: 1
-33
View File
@@ -1,33 +0,0 @@
name: "Pull: E2E Tests"
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
e2e_tests:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup go
uses: actions/setup-go@v4
with:
go-version: '>=1.20'
check-latest: true
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 20
- name: build
run: make deps-frontend frontend deps-backend
- name: Install playwright browsers
run: npx playwright install --with-deps
- name: run tests
run: timeout -s ABRT 40m make test-e2e-sqlite
env:
GOPROXY: https://goproxy.io
GOSUMDB: sum.golang.org
USE_REPO_TEST_DIR: 1
+1 -1
View File
@@ -5,7 +5,7 @@ ignoreFiles:
- "**/*.go"
overrides:
- files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console/*"]
- files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console.css"]
rules:
scale-unlimited/declaration-strict-value: null
- files: ["**/chroma/*", "**/codemirror/*"]
+2 -2
View File
@@ -1,5 +1,5 @@
#Build stage
FROM docker.io/library/golang:1.20-alpine3.17 AS build-env
FROM docker.io/library/golang:1.20-alpine3.18 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@@ -23,7 +23,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go
FROM docker.io/library/alpine:3.17
FROM docker.io/library/alpine:3.18
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000
+2 -2
View File
@@ -1,5 +1,5 @@
#Build stage
FROM docker.io/library/golang:1.20-alpine3.17 AS build-env
FROM docker.io/library/golang:1.20-alpine3.18 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@@ -23,7 +23,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go
FROM docker.io/library/alpine:3.17
FROM docker.io/library/alpine:3.18
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000
+1
View File
@@ -50,3 +50,4 @@ Eddie Yang <576951401@qq.com> (@yp05327)
Dong Ge <gedong_1994@163.com> (@sillyguodong)
Xinyi Gong <hestergong@gmail.com> (@HesterG)
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
Gary Moon <gary@garymoon.net> (@garymoon)
+2 -2
View File
@@ -394,7 +394,7 @@ lint-go:
lint-go-fix:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
# workaround step for the lint-backend-windows CI task because 'go run' can not
# workaround step for the lint-go-windows CI task because 'go run' can not
# have distinct GOOS/GOARCH for its build and run steps
.PHONY: lint-go-windows
lint-go-windows:
@@ -409,7 +409,7 @@ lint-go-vet:
.PHONY: lint-editorconfig
lint-editorconfig:
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .github/workflows
.PHONY: watch
watch:
+19 -1
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/migrations"
@@ -32,7 +33,7 @@ var CmdMigrateStorage = cli.Command{
cli.StringFlag{
Name: "type, t",
Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages'",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
},
cli.StringFlag{
Name: "storage, s",
@@ -134,6 +135,22 @@ func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) erro
})
}
func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, task *actions_model.ActionTask) error {
if task.LogExpired {
// the log has been cleared
return nil
}
if !task.LogInStorage {
// running tasks store logs in DBFS
return nil
}
p := task.LogFilename
_, err := storage.Copy(dstStorage, p, storage.Actions, p)
return err
})
}
func runMigrateStorage(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
defer cancel()
@@ -201,6 +218,7 @@ func runMigrateStorage(ctx *cli.Context) error {
"repo-avatars": migrateRepoAvatars,
"repo-archivers": migrateRepoArchivers,
"packages": migratePackages,
"actions-log": migrateActionsLog,
}
tp := strings.ToLower(ctx.String("type"))
+2 -1
View File
@@ -52,7 +52,7 @@ After=network.target
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
# LimitNOFILE=524288:524288
RestartSec=2s
Type=simple
Type=notify
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
@@ -62,6 +62,7 @@ WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
WatchdogSec=30s
# If you install Git to directory prefix other than default PATH (which happens
# for example if you install other versions of Git side-to-side with
# distribution version), uncomment below line and add that prefix to PATH
+18 -6
View File
@@ -940,6 +940,9 @@ ROUTER = console
;; Force ssh:// clone url instead of scp-style uri when default SSH port is used
;USE_COMPAT_SSH_URI = false
;;
;; Value for the "go get" request returns the repository url as https or ssh, default is https
;GO_GET_CLONE_URL_PROTOCOL = https
;;
;; Close issues as long as a commit on any branch marks it as fixed
;; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects, repo.packages, repo.actions.
;DISABLED_REPO_UNITS =
@@ -1430,7 +1433,7 @@ ROUTER = console
;; Batch size to send for batched queues
;BATCH_LENGTH = 20
;;
;; Connection string for redis queues this will store the redis connection string.
;; Connection string for redis queues this will store the redis or redis-cluster connection string.
;; When `TYPE` is `persistable-channel`, this provides a directory for the underlying leveldb
;; or additional options of the form `leveldb://path/to/db?option=value&....`, and will override `DATADIR`.
;CONN_STR = "redis://127.0.0.1:6379/0"
@@ -1702,8 +1705,9 @@ ROUTER = console
;; For "memory" only, GC interval in seconds, default is 60
;INTERVAL = 60
;;
;; For "redis" and "memcache", connection host address
;; For "redis", "redis-cluster" and "memcache", connection host address
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
;; memcache: `127.0.0.1:11211`
;; twoqueue: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000`
;HOST =
@@ -1735,7 +1739,7 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Either "memory", "file", "redis", "db", "mysql", "couchbase", "memcache" or "postgres"
;; Either "memory", "file", "redis", "redis-cluster", "db", "mysql", "couchbase", "memcache" or "postgres"
;; Default is "memory". "db" will reuse the configuration in [database]
;PROVIDER = memory
;;
@@ -1743,6 +1747,7 @@ ROUTER = console
;; memory: doesn't have any config yet
;; file: session file path, e.g. `data/sessions`
;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_.
;;
@@ -1778,16 +1783,19 @@ ROUTER = console
;; Max Width and Height of uploaded avatars.
;; This is to limit the amount of RAM used when resizing the image.
;AVATAR_MAX_WIDTH = 4096
;AVATAR_MAX_HEIGHT = 3072
;AVATAR_MAX_HEIGHT = 4096
;;
;; The multiplication factor for rendered avatar images.
;; Larger values result in finer rendering on HiDPI devices.
;AVATAR_RENDERED_SIZE_FACTOR = 3
;AVATAR_RENDERED_SIZE_FACTOR = 2
;;
;; Maximum allowed file size for uploaded avatars.
;; This is to limit the amount of RAM used when resizing the image.
;AVATAR_MAX_FILE_SIZE = 1048576
;;
;; If the uploaded file is not larger than this byte size, the image will be used as is, without resizing/converting.
;AVATAR_MAX_ORIGIN_SIZE = 262144
;;
;; Chinese users can choose "duoshuo"
;; or a custom avatar source, like: http://cn.gravatar.com/avatar/
;GRAVATAR_SOURCE = gravatar
@@ -2373,7 +2381,7 @@ ROUTER = console
;QUEUE_LENGTH = 1000
;;
;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`.
;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`.
;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2447,6 +2455,8 @@ ROUTER = console
;LIMIT_TOTAL_OWNER_COUNT = -1
;; Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_TOTAL_OWNER_SIZE = -1
;; Maximum size of an Alpine upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_ALPINE = -1
;; Maximum size of a Cargo upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_CARGO = -1
;; Maximum size of a Chef upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
@@ -2463,6 +2473,8 @@ ROUTER = console
;LIMIT_SIZE_DEBIAN = -1
;; Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_GENERIC = -1
;; Maximum size of a Go upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_GO = -1
;; Maximum size of a Helm upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_HELM = -1
;; Maximum size of a Maven upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
@@ -95,6 +95,8 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build
HTTP protocol.
- `USE_COMPAT_SSH_URI`: **false**: Force ssh:// clone url instead of scp-style uri when
default SSH port is used.
- `GO_GET_CLONE_URL_PROTOCOL`: **https**: Value for the "go get" request returns the repository url as https or ssh
default is https.
- `ACCESS_CONTROL_ALLOW_ORIGIN`: **\<empty\>**: Value for Access-Control-Allow-Origin header,
default is not to present. **WARNING**: This maybe harmful to you website if you do not
give it a right value.
@@ -485,7 +487,7 @@ Configuration at `[queue]` will set defaults for queues with overrides for indiv
- `DATADIR`: **queues/common**: Base DataDir for storing level queues. `DATADIR` for individual queues can be set in `queue.name` sections. Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
- `LENGTH`: **100**: Maximal queue size before channel queues block
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. For `redis-cluster` use `redis+cluster://127.0.0.1:6379/0`. Options can be set using query params. Similarly, LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
- `QUEUE_NAME`: **_queue**: The suffix for default redis and disk queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overridden in the specific `queue.name` section.
- `SET_NAME`: **_unique**: The suffix that will be added to the default redis and disk queue `set` name for unique queues. Individual queues will default to **`name`**`QUEUE_NAME`_`SET_NAME`_ but can be overridden in the specific `queue.name` section.
- `MAX_WORKERS`: **10**: Maximum number of worker go-routines for the queue.
@@ -757,10 +759,11 @@ and
## Cache (`cache`)
- `ENABLED`: **true**: Enable the cache.
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `redis-cluster`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory and twoqueue cache only.
- `HOST`: **\<empty\>**: Connection string for `redis` and `memcache`. For `twoqueue` sets configuration for the queue.
- `HOST`: **\<empty\>**: Connection string for `redis`, `redis-cluster` and `memcache`. For `twoqueue` sets configuration for the queue.
- Redis: `redis://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
- Redis-cluster `redis+cluster://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
- Memcache: `127.0.0.1:9090;127.0.0.1:9091`
- TwoQueue LRU cache: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` representing the maximum number of objects stored in the cache.
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
@@ -773,7 +776,7 @@ and
## Session (`session`)
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, redis-cluster, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
@@ -793,9 +796,10 @@ and
- `AVATAR_STORAGE_TYPE`: **default**: Storage type defined in `[storage.xxx]`. Default is `default` which will read `[storage]` if no section `[storage]` will be a type `local`.
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store user avatar image files.
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.
- `AVATAR_RENDERED_SIZE_FACTOR`: **3**: The multiplication factor for rendered avatar images. Larger values result in finer rendering on HiDPI devices.
- `AVATAR_MAX_HEIGHT`: **4096**: Maximum avatar image height in pixels.
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1MiB): Maximum avatar image file size in bytes.
- `AVATAR_MAX_ORIGIN_SIZE`: **262144** (256KiB): If the uploaded file is not larger than this byte size, the image will be used as is, without resizing/converting.
- `AVATAR_RENDERED_SIZE_FACTOR`: **2**: The multiplication factor for rendered avatar images. Larger values result in finer rendering on HiDPI devices.
- `REPOSITORY_AVATAR_STORAGE_TYPE`: **default**: Storage type defined in `[storage.xxx]`. Default is `default` which will read `[storage]` if no section `[storage]` will be a type `local`.
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: Path to store repository avatar image files.
@@ -1019,7 +1023,7 @@ Default templates for project boards:
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **@every 8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap.
- `OLDER_THAN`: **8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap.
#### Cron - Check for new Gitea versions (`cron.update_checker`)
@@ -1035,7 +1039,7 @@ Default templates for project boards:
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **@every 8760h**: any system notice older than this expression will be deleted from database.
- `OLDER_THAN`: **8760h**: any system notice older than this expression will be deleted from database.
#### Cron - Garbage collect LFS pointers in repositories (`cron.gc_lfs`)
@@ -1184,7 +1188,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0`.
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0` or `redis+cluster://123@127.0.0.1:6379/0`.
## Migrations (`migrations`)
@@ -1214,6 +1218,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
- `CHUNKED_UPLOAD_PATH`: **tmp/package-upload**: Path for chunked uploads. Defaults to `APP_DATA_PATH` + `tmp/package-upload`
- `LIMIT_TOTAL_OWNER_COUNT`: **-1**: Maximum count of package versions a single owner can have (`-1` means no limits)
- `LIMIT_TOTAL_OWNER_SIZE`: **-1**: Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_ALPINE`: **-1**: Maximum size of an Alpine upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_CARGO`: **-1**: Maximum size of a Cargo upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_CHEF`: **-1**: Maximum size of a Chef upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_COMPOSER`: **-1**: Maximum size of a Composer upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
@@ -1222,6 +1227,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
- `LIMIT_SIZE_CONTAINER`: **-1**: Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_DEBIAN`: **-1**: Maximum size of a Debian upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_GENERIC`: **-1**: Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_GO`: **-1**: Maximum size of a Go upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_HELM`: **-1**: Maximum size of a Helm upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_MAVEN`: **-1**: Maximum size of a Maven upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_NPM`: **-1**: Maximum size of a npm upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
@@ -214,8 +214,8 @@ menu:
- `AVATAR_STORAGE_TYPE`: **local**: 头像存储类型,可以为 `local``minio`,分别支持本地文件系统和 minio 兼容的API。
- `AVATAR_UPLOAD_PATH`: **data/avatars**: 存储头像的文件系统路径。
- `AVATAR_MAX_WIDTH`: **4096**: 头像最大宽度,单位像素。
- `AVATAR_MAX_HEIGHT`: **3072**: 头像最大高度,单位像素。
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): 头像最大大小。
- `AVATAR_MAX_HEIGHT`: **4096**: 头像最大高度,单位像素。
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1MiB): 头像最大大小。
- `REPOSITORY_AVATAR_STORAGE_TYPE`: **local**: 仓库头像存储类型,可以为 `local``minio`,分别支持本地文件系统和 minio 兼容的API。
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: 存储仓库头像的路径。
@@ -1,6 +1,6 @@
---
date: "2019-02-14T11:51:04+08:00"
title: "横向对比 Gitea 与其它 Git 托管工具"
title: "对比 Gitea 与其它 Git 托管工具"
slug: "comparison"
weight: 5
toc: false
@@ -15,7 +15,7 @@ menu:
identifier: "comparison"
---
# 横向对比 Gitea 与其它 Git 托管工具
# 对比 Gitea 与其它 Git 托管工具
这里列出了 Gitea 与其它一些 Git 托管工具之间的异同,以便确认 Gitea 是否能够满足您的需求。
@@ -80,6 +80,7 @@ git --version
Create a user to run Gitea (e.g. `git`)
```sh
# On Ubuntu/Debian:
adduser \
--system \
--shell /bin/bash \
@@ -88,6 +89,17 @@ adduser \
--disabled-password \
--home /home/git \
git
# On Fedora/RHEL/CentOS:
groupadd --system git
adduser \
--system \
--shell /bin/bash \
--comment 'Git Version Control' \
--gid git \
--home-dir /home/git \
--create-home \
git
```
### Create required directory structure
@@ -74,6 +74,7 @@ git --version
创建用户(推荐使用名称 `git`
```sh
# On Ubuntu/Debian:
adduser \
--system \
--shell /bin/bash \
@@ -82,6 +83,17 @@ adduser \
--disabled-password \
--home /home/git \
git
# On Fedora/RHEL/CentOS:
groupadd --system git
adduser \
--system \
--shell /bin/bash \
--comment 'Git Version Control' \
--gid git \
--home-dir /home/git \
--create-home \
git
```
### 创建工作路径
@@ -85,7 +85,7 @@ And you can see the new runner in the management page:
![view runner](/images/usage/actions/view-runner.png)
You can find more information by visiting [Act runner]({{< relref "doc/actions/act-runner.en-us.md" >}}).
You can find more information by visiting [Act runner]({{< relref "doc/usage/actions/act-runner.en-us.md" >}}).
### Use Actions
@@ -0,0 +1,133 @@
---
date: "2023-03-25T00:00:00+00:00"
title: "Alpine Packages Repository"
slug: "packages/alpine"
draft: false
toc: false
menu:
sidebar:
parent: "packages"
name: "Alpine"
weight: 4
identifier: "alpine"
---
# Alpine Packages Repository
Publish [Alpine](https://pkgs.alpinelinux.org/) packages for your user or organization.
**Table of Contents**
{{< toc >}}
## Requirements
To work with the Alpine registry, you need to use a HTTP client like `curl` to upload and a package manager like `apk` to consume packages.
The following examples use `apk`.
## Configuring the package registry
To register the Alpine registry add the url to the list of known apk sources (`/etc/apk/repositories`):
```
https://gitea.example.com/api/packages/{owner}/alpine/<branch>/<repository>
```
| Placeholder | Description |
| ------------ | ----------- |
| `owner` | The owner of the packages. |
| `branch` | The branch to use. |
| `repository` | The repository to use. |
If the registry is private, provide credentials in the url. You can use a password or a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}):
```
https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/alpine/<branch>/<repository>
```
The Alpine registry files are signed with a RSA key which must be known to apk. Download the public key and store it in `/etc/apk/keys/`:
```shell
curl -JO https://gitea.example.com/api/packages/{owner}/alpine/key
```
Afterwards update the local package index:
```shell
apk update
```
## Publish a package
To publish an Alpine package (`*.apk`), perform a HTTP `PUT` operation with the package content in the request body.
```
PUT https://gitea.example.com/api/packages/{owner}/alpine/{branch}/{repository}
```
| Parameter | Description |
| ------------ | ----------- |
| `owner` | The owner of the package. |
| `branch` | The branch may match the release version of the OS, ex: `v3.17`. |
| `repository` | The repository can be used [to group packages](https://wiki.alpinelinux.org/wiki/Repositories) or just `main` or similar. |
Example request using HTTP Basic authentication:
```shell
curl --user your_username:your_password_or_token \
--upload-file path/to/file.apk \
https://gitea.example.com/api/packages/testuser/alpine/v3.17/main
```
If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}) instead of the password.
You cannot publish a file with the same name twice to a package. You must delete the existing package file first.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
| `201 Created` | The package has been published. |
| `400 Bad Request` | The package name, version, branch, repository or architecture are invalid. |
| `409 Conflict` | A package file with the same combination of parameters exist already in the package. |
## Delete a package
To delete an Alpine package perform a HTTP `DELETE` operation. This will delete the package version too if there is no file left.
```
DELETE https://gitea.example.com/api/packages/{owner}/alpine/{branch}/{repository}/{architecture}/{filename}
```
| Parameter | Description |
| -------------- | ----------- |
| `owner` | The owner of the package. |
| `branch` | The branch to use. |
| `repository` | The repository to use. |
| `architecture` | The package architecture. |
| `filename` | The file to delete.
Example request using HTTP Basic authentication:
```shell
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/alpine/v3.17/main/test-package-1.0.0.apk
```
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
| `204 No Content` | Success |
| `404 Not Found` | The package or file was not found. |
## Install a package
To install a package from the Alpine registry, execute the following commands:
```shell
# use latest version
apk add {package_name}
# use specific version
apk add {package_name}={package_version}
```
@@ -83,7 +83,7 @@ curl --user your_username:your_password_or_token \
If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}) instead of the password.
You cannot publish a file with the same name twice to a package. You must delete the existing package version first.
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -115,7 +115,7 @@ curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/debian/pools/bionic/main/test-package/1.0.0/amd64
```
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -51,7 +51,7 @@ curl --user your_username:your_password_or_token \
If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}) instead of the password.
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -83,7 +83,7 @@ curl --user your_username:your_token_or_password \
https://gitea.example.com/api/packages/testuser/generic/test_package/1.0.0/file.bin
```
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -111,7 +111,7 @@ curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/generic/test_package/1.0.0
```
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -140,7 +140,7 @@ curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/generic/test_package/1.0.0/file.bin
```
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -0,0 +1,77 @@
---
date: "2023-05-10T00:00:00+00:00"
title: "Go Packages Repository"
slug: "go"
weight: 45
draft: false
toc: false
menu:
sidebar:
parent: "packages"
name: "Go"
weight: 45
identifier: "go"
---
# Go Packages Repository
Publish Go packages for your user or organization.
**Table of Contents**
{{< toc >}}
## Publish a package
To publish a Go package perform a HTTP `PUT` operation with the package content in the request body.
You cannot publish a package if a package of the same name and version already exists. You must delete the existing package first.
The package must follow the [documented structure](https://go.dev/ref/mod#zip-files).
```
PUT https://gitea.example.com/api/packages/{owner}/go/upload
```
| Parameter | Description |
| --------- | ----------- |
| `owner` | The owner of the package. |
To authenticate to the package registry, you need to provide [custom HTTP headers or use HTTP Basic authentication]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}):
```shell
curl --user your_username:your_password_or_token \
--upload-file path/to/file.zip \
https://gitea.example.com/api/packages/testuser/go/upload
```
If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}) instead of the password.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
| `201 Created` | The package has been published. |
| `400 Bad Request` | The package is invalid. |
| `409 Conflict` | A package with the same name exist already. |
## Install a package
To install a Go package instruct Go to use the package registry as proxy:
```shell
# use latest version
GOPROXY=https://gitea.example.com/api/packages/{owner}/go go install {package_name}
# or
GOPROXY=https://gitea.example.com/api/packages/{owner}/go go install {package_name}@latest
# use specific version
GOPROXY=https://gitea.example.com/api/packages/{owner}/go go install {package_name}@{package_version}
```
| Parameter | Description |
| ----------------- | ----------- |
| `owner` | The owner of the package. |
| `package_name` | The package name. |
| `package_version` | The package version. |
If the owner of the packages is private you need to [provide credentials](https://go.dev/ref/mod#private-module-proxy-auth).
More information about the `GOPROXY` environment variable and how to protect against data leaks can be found in [the documentation](https://go.dev/ref/mod#private-modules).
@@ -27,6 +27,7 @@ The following package managers are currently supported:
| Name | Language | Package client |
| ---- | -------- | -------------- |
| [Alpine]({{< relref "doc/usage/packages/alpine.en-us.md" >}}) | - | `apk` |
| [Cargo]({{< relref "doc/usage/packages/cargo.en-us.md" >}}) | Rust | `cargo` |
| [Chef]({{< relref "doc/usage/packages/chef.en-us.md" >}}) | - | `knife` |
| [Composer]({{< relref "doc/usage/packages/composer.en-us.md" >}}) | PHP | `composer` |
@@ -35,6 +36,7 @@ The following package managers are currently supported:
| [Container]({{< relref "doc/usage/packages/container.en-us.md" >}}) | - | any OCI compliant client |
| [Debian]({{< relref "doc/usage/packages/debian.en-us.md" >}}) | - | `apt` |
| [Generic]({{< relref "doc/usage/packages/generic.en-us.md" >}}) | - | any HTTP client |
| [Go]({{< relref "doc/usage/packages/go.en-us.md" >}}) | Go | `go` |
| [Helm]({{< relref "doc/usage/packages/helm.en-us.md" >}}) | - | any HTTP client, `cm-push` |
| [Maven]({{< relref "doc/usage/packages/maven.en-us.md" >}}) | Java | `mvn`, `gradle` |
| [npm]({{< relref "doc/usage/packages/npm.en-us.md" >}}) | JavaScript | `npm`, `yarn`, `pnpm` |
+2 -2
View File
@@ -69,7 +69,7 @@ curl --user your_username:your_password_or_token \
If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/development/api-usage.en-us.md#authentication" >}}) instead of the password.
You cannot publish a file with the same name twice to a package. You must delete the existing package version first.
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -99,7 +99,7 @@ curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
```
The server reponds with the following HTTP Status codes.
The server responds with the following HTTP Status codes.
| HTTP Status Code | Meaning |
| ----------------- | ------- |
@@ -9,7 +9,7 @@ menu:
sidebar:
parent: "packages"
name: "Storage"
weight: 5
weight: 2
identifier: "storage"
---
-1
View File
@@ -1,7 +1,6 @@
<!DOCTYPE html>
<html{{ with site.LanguageCode | default site.Language.Lang }} lang="{{ . }}"{{ end }}>
<head>
<meta charset="utf-8">
<title>{{ .Permalink }}</title>
<link rel="canonical" href="{{ .Permalink }}">
<meta name="robots" content="noindex">
+3 -10
View File
@@ -10,7 +10,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -45,6 +44,9 @@ func (runs RunList) LoadTriggerUser(ctx context.Context) error {
run.TriggerUser = user_model.NewActionsUser()
} else {
run.TriggerUser = users[run.TriggerUserID]
if run.TriggerUser == nil {
run.TriggerUser = user_model.NewGhostUser()
}
}
}
return nil
@@ -66,7 +68,6 @@ type FindRunOptions struct {
db.ListOptions
RepoID int64
OwnerID int64
IsClosed util.OptionalBool
WorkflowFileName string
TriggerUserID int64
Approved bool // not util.OptionalBool, it works only when it's true
@@ -80,14 +81,6 @@ func (opts FindRunOptions) toConds() builder.Cond {
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.IsClosed.IsFalse() {
cond = cond.And(builder.Eq{"status": StatusWaiting}.Or(
builder.Eq{"status": StatusRunning}))
} else if opts.IsClosed.IsTrue() {
cond = cond.And(
builder.Neq{"status": StatusWaiting}.And(
builder.Neq{"status": StatusRunning}))
}
if opts.WorkflowFileName != "" {
cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowFileName})
}
+17 -5
View File
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/shared/types"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
@@ -28,7 +29,7 @@ type ActionRunner struct {
Version string `xorm:"VARCHAR(64)"`
OwnerID int64 `xorm:"index"` // org level runner, 0 means system
Owner *user_model.User `xorm:"-"`
RepoID int64 `xorm:"index"` // repo level runner, if orgid also is zero, then it's a global
RepoID int64 `xorm:"index"` // repo level runner, if OwnerID also is zero, then it's a global
Repo *repo_model.Repository `xorm:"-"`
Description string `xorm:"TEXT"`
Base int // 0 native 1 docker 2 virtual machine
@@ -52,14 +53,25 @@ type ActionRunner struct {
Deleted timeutil.TimeStamp `xorm:"deleted"`
}
func (r *ActionRunner) OwnType() string {
// BelongsToOwnerName before calling, should guarantee that all attributes are loaded
func (r *ActionRunner) BelongsToOwnerName() string {
if r.RepoID != 0 {
return fmt.Sprintf("Repo(%s)", r.Repo.FullName())
return r.Repo.FullName()
}
if r.OwnerID != 0 {
return fmt.Sprintf("Org(%s)", r.Owner.Name)
return r.Owner.Name
}
return "Global"
return ""
}
func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
if r.RepoID != 0 {
return types.OwnerTypeRepository
}
if r.OwnerID != 0 {
return types.OwnerTypeOrganization
}
return types.OwnerTypeSystemGlobal
}
func (r *ActionRunner) Status() runnerv1.RunnerStatus {
+50
View File
@@ -23,6 +23,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
"xorm.io/xorm"
)
@@ -240,6 +241,55 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp
return statuses, count, db.GetEngine(ctx).In("id", ids).Find(&statuses)
}
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) {
type result struct {
ID int64
RepoID int64
}
results := make([]result, 0, len(repoIDsToLatestCommitSHAs))
sess := db.GetEngine(ctx).Table(&CommitStatus{})
// Create a disjunction of conditions for each repoID and SHA pair
conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs))
for repoID, sha := range repoIDsToLatestCommitSHAs {
conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha})
}
sess = sess.Where(builder.Or(conds...)).
Select("max( id ) as id, repo_id").
GroupBy("context_hash, repo_id").OrderBy("max( id ) desc")
sess = db.SetSessionPagination(sess, &listOptions)
err := sess.Find(&results)
if err != nil {
return nil, err
}
ids := make([]int64, 0, len(results))
repoStatuses := make(map[int64][]*CommitStatus)
for _, result := range results {
ids = append(ids, result.ID)
}
statuses := make([]*CommitStatus, 0, len(ids))
if len(ids) > 0 {
err = db.GetEngine(ctx).In("id", ids).Find(&statuses)
if err != nil {
return nil, err
}
// Group the statuses by repo ID
for _, status := range statuses {
repoStatuses[status.RepoID] = append(repoStatuses[status.RepoID], status)
}
}
return repoStatuses, nil
}
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) {
start := timeutil.TimeStampNow().AddDuration(-before)
+2 -2
View File
@@ -63,8 +63,8 @@ func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_mo
Where(builder.Eq{"team_id": opts.TeamID}),
)
}
if opts.PageSize > 0 && opts.Page > -1 {
sess = sess.Limit(opts.PageSize, opts.Page*opts.PageSize)
if opts.PageSize > 0 && opts.Page > 0 {
sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if err := sess.OrderBy("full_name, name").Find(&members); err != nil {
return nil, err
+53
View File
@@ -0,0 +1,53 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package alpine
import (
"context"
packages_model "code.gitea.io/gitea/models/packages"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
)
// GetBranches gets all available branches
func GetBranches(ctx context.Context, ownerID int64) ([]string, error) {
return packages_model.GetDistinctPropertyValues(
ctx,
packages_model.TypeAlpine,
ownerID,
packages_model.PropertyTypeFile,
alpine_module.PropertyBranch,
nil,
)
}
// GetRepositories gets all available repositories for the given branch
func GetRepositories(ctx context.Context, ownerID int64, branch string) ([]string, error) {
return packages_model.GetDistinctPropertyValues(
ctx,
packages_model.TypeAlpine,
ownerID,
packages_model.PropertyTypeFile,
alpine_module.PropertyRepository,
&packages_model.DistinctPropertyDependency{
Name: alpine_module.PropertyBranch,
Value: branch,
},
)
}
// GetArchitectures gets all available architectures for the given repository
func GetArchitectures(ctx context.Context, ownerID int64, repository string) ([]string, error) {
return packages_model.GetDistinctPropertyValues(
ctx,
packages_model.TypeAlpine,
ownerID,
packages_model.PropertyTypeFile,
alpine_module.PropertyArchitecture,
&packages_model.DistinctPropertyDependency{
Name: alpine_module.PropertyRepository,
Value: repository,
},
)
}
+30 -32
View File
@@ -88,44 +88,42 @@ func SearchLatestPackages(ctx context.Context, opts *PackageSearchOptions) ([]*p
// GetDistributions gets all available distributions
func GetDistributions(ctx context.Context, ownerID int64) ([]string, error) {
return getDistinctPropertyValues(ctx, ownerID, "", debian_module.PropertyDistribution)
return packages.GetDistinctPropertyValues(
ctx,
packages.TypeDebian,
ownerID,
packages.PropertyTypeFile,
debian_module.PropertyDistribution,
nil,
)
}
// GetComponents gets all available components for the given distribution
func GetComponents(ctx context.Context, ownerID int64, distribution string) ([]string, error) {
return getDistinctPropertyValues(ctx, ownerID, distribution, debian_module.PropertyComponent)
return packages.GetDistinctPropertyValues(
ctx,
packages.TypeDebian,
ownerID,
packages.PropertyTypeFile,
debian_module.PropertyComponent,
&packages.DistinctPropertyDependency{
Name: debian_module.PropertyDistribution,
Value: distribution,
},
)
}
// GetArchitectures gets all available architectures for the given distribution
func GetArchitectures(ctx context.Context, ownerID int64, distribution string) ([]string, error) {
return getDistinctPropertyValues(ctx, ownerID, distribution, debian_module.PropertyArchitecture)
}
func getDistinctPropertyValues(ctx context.Context, ownerID int64, distribution, propName string) ([]string, error) {
var cond builder.Cond = builder.Eq{
"package_property.ref_type": packages.PropertyTypeFile,
"package_property.name": propName,
"package.type": packages.TypeDebian,
"package.owner_id": ownerID,
}
if distribution != "" {
innerCond := builder.
Expr("pp.ref_id = package_property.ref_id").
And(builder.Eq{
"pp.ref_type": packages.PropertyTypeFile,
"pp.name": debian_module.PropertyDistribution,
"pp.value": distribution,
})
cond = cond.And(builder.Exists(builder.Select("pp.ref_id").From("package_property pp").Where(innerCond)))
}
values := make([]string, 0, 5)
return values, db.GetEngine(ctx).
Table("package_property").
Distinct("package_property.value").
Join("INNER", "package_file", "package_file.id = package_property.ref_id").
Join("INNER", "package_version", "package_version.id = package_file.version_id").
Join("INNER", "package", "package.id = package_version.package_id").
Where(cond).
Find(&values)
return packages.GetDistinctPropertyValues(
ctx,
packages.TypeDebian,
ownerID,
packages.PropertyTypeFile,
debian_module.PropertyArchitecture,
&packages.DistinctPropertyDependency{
Name: debian_module.PropertyDistribution,
Value: distribution,
},
)
}
+5
View File
@@ -12,6 +12,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/packages/alpine"
"code.gitea.io/gitea/modules/packages/cargo"
"code.gitea.io/gitea/modules/packages/chef"
"code.gitea.io/gitea/modules/packages/composer"
@@ -136,6 +137,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
var metadata interface{}
switch p.Type {
case TypeAlpine:
metadata = &alpine.VersionMetadata{}
case TypeCargo:
metadata = &cargo.Metadata{}
case TypeChef:
@@ -152,6 +155,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
metadata = &debian.Metadata{}
case TypeGeneric:
// generic packages have no metadata
case TypeGo:
// go packages have no metadata
case TypeHelm:
metadata = &helm.Metadata{}
case TypeNuGet:
+12
View File
@@ -30,6 +30,7 @@ type Type string
// List of supported packages
const (
TypeAlpine Type = "alpine"
TypeCargo Type = "cargo"
TypeChef Type = "chef"
TypeComposer Type = "composer"
@@ -38,6 +39,7 @@ const (
TypeContainer Type = "container"
TypeDebian Type = "debian"
TypeGeneric Type = "generic"
TypeGo Type = "go"
TypeHelm Type = "helm"
TypeMaven Type = "maven"
TypeNpm Type = "npm"
@@ -51,6 +53,7 @@ const (
)
var TypeList = []Type{
TypeAlpine,
TypeCargo,
TypeChef,
TypeComposer,
@@ -59,6 +62,7 @@ var TypeList = []Type{
TypeContainer,
TypeDebian,
TypeGeneric,
TypeGo,
TypeHelm,
TypeMaven,
TypeNpm,
@@ -74,6 +78,8 @@ var TypeList = []Type{
// Name gets the name of the package type
func (pt Type) Name() string {
switch pt {
case TypeAlpine:
return "Alpine"
case TypeCargo:
return "Cargo"
case TypeChef:
@@ -90,6 +96,8 @@ func (pt Type) Name() string {
return "Debian"
case TypeGeneric:
return "Generic"
case TypeGo:
return "Go"
case TypeHelm:
return "Helm"
case TypeMaven:
@@ -117,6 +125,8 @@ func (pt Type) Name() string {
// SVGName gets the name of the package type svg image
func (pt Type) SVGName() string {
switch pt {
case TypeAlpine:
return "gitea-alpine"
case TypeCargo:
return "gitea-cargo"
case TypeChef:
@@ -133,6 +143,8 @@ func (pt Type) SVGName() string {
return "gitea-debian"
case TypeGeneric:
return "octicon-package"
case TypeGo:
return "gitea-go"
case TypeHelm:
return "gitea-helm"
case TypeMaven:
+38
View File
@@ -7,6 +7,8 @@ import (
"context"
"code.gitea.io/gitea/models/db"
"xorm.io/builder"
)
func init() {
@@ -81,3 +83,39 @@ func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}
type DistinctPropertyDependency struct {
Name string
Value string
}
// GetDistinctPropertyValues returns all distinct property values for a given type.
// Optional: Search only in dependence of another property.
func GetDistinctPropertyValues(ctx context.Context, packageType Type, ownerID int64, refType PropertyType, propertyName string, dep *DistinctPropertyDependency) ([]string, error) {
var cond builder.Cond = builder.Eq{
"package_property.ref_type": refType,
"package_property.name": propertyName,
"package.type": packageType,
"package.owner_id": ownerID,
}
if dep != nil {
innerCond := builder.
Expr("pp.ref_id = package_property.ref_id").
And(builder.Eq{
"pp.ref_type": refType,
"pp.name": dep.Name,
"pp.value": dep.Value,
})
cond = cond.And(builder.Exists(builder.Select("pp.ref_id").From("package_property pp").Where(innerCond)))
}
values := make([]string, 0, 5)
return values, db.GetEngine(ctx).
Table("package_property").
Distinct("package_property.value").
Join("INNER", "package_file", "package_file.id = package_property.ref_id").
Join("INNER", "package_version", "package_version.id = package_file.version_id").
Join("INNER", "package", "package.id = package_version.package_id").
Where(cond).
Find(&values)
}
+21 -15
View File
@@ -149,8 +149,8 @@ type Repository struct {
IsEmpty bool `xorm:"INDEX"`
IsArchived bool `xorm:"INDEX"`
IsMirror bool `xorm:"INDEX"`
*Mirror `xorm:"-"`
Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"`
Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"`
RenderingMetas map[string]string `xorm:"-"`
DocumentRenderingMetas map[string]string `xorm:"-"`
@@ -596,16 +596,9 @@ func ComposeHTTPSCloneURL(owner, repo string) string {
return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
}
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
repoName := repo.Name
if isWiki {
repoName += ".wiki"
}
func ComposeSSHCloneURL(ownerName, repoName string) string {
sshUser := setting.SSH.User
cl := new(CloneLink)
// if we have a ipv6 literal we need to put brackets around it
// for the git cloning to work.
sshDomain := setting.SSH.Domain
@@ -615,12 +608,25 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
}
if setting.SSH.Port != 22 {
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
} else if setting.Repository.UseCompatSSHURI {
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
} else {
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser,
net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)),
url.PathEscape(ownerName),
url.PathEscape(repoName))
}
if setting.Repository.UseCompatSSHURI {
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(ownerName), url.PathEscape(repoName))
}
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(ownerName), url.PathEscape(repoName))
}
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
repoName := repo.Name
if isWiki {
repoName += ".wiki"
}
cl := new(CloneLink)
cl.SSH = ComposeSSHCloneURL(repo.OwnerName, repoName)
cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
return cl
}
+1 -1
View File
@@ -59,7 +59,7 @@ func newSecret(ownerID, repoID int64, name, data string) *Secret {
// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database
func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) {
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, strings.TrimSpace(data))
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
if err != nil {
return nil, err
}
+29
View File
@@ -0,0 +1,29 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package types
import "code.gitea.io/gitea/modules/translation"
type OwnerType string
const (
OwnerTypeSystemGlobal = "system-global"
OwnerTypeIndividual = "individual"
OwnerTypeRepository = "repository"
OwnerTypeOrganization = "organization"
)
func (o OwnerType) LocaleString(locale translation.Locale) string {
switch o {
case OwnerTypeSystemGlobal:
return locale.Tr("concept_system_global")
case OwnerTypeIndividual:
return locale.Tr("concept_user_individual")
case OwnerTypeRepository:
return locale.Tr("concept_code_repository")
case OwnerTypeOrganization:
return locale.Tr("concept_user_organization")
}
return locale.Tr("unknown")
}
+55 -15
View File
@@ -5,13 +5,14 @@ package avatar
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
"image/png"
_ "image/gif" // for processing gif images
_ "image/jpeg" // for processing jpeg images
_ "image/png" // for processing png images
"code.gitea.io/gitea/modules/avatar/identicon"
"code.gitea.io/gitea/modules/setting"
@@ -22,8 +23,11 @@ import (
_ "golang.org/x/image/webp" // for processing webp images
)
// AvatarSize returns avatar's size
const AvatarSize = 290
// DefaultAvatarSize is the target CSS pixel size for avatar generation. It is
// multiplied by setting.Avatar.RenderedSizeFactor and the resulting size is the
// usual size of avatar image saved on server, unless the original file is smaller
// than the size after resizing.
const DefaultAvatarSize = 256
// RandomImageSize generates and returns a random avatar image unique to input data
// in custom size (height and width).
@@ -39,28 +43,44 @@ func RandomImageSize(size int, data []byte) (image.Image, error) {
// RandomImage generates and returns a random avatar image unique to input data
// in default size (height and width).
func RandomImage(data []byte) (image.Image, error) {
return RandomImageSize(AvatarSize, data)
return RandomImageSize(DefaultAvatarSize*setting.Avatar.RenderedSizeFactor, data)
}
// Prepare accepts a byte slice as input, validates it contains an image of an
// acceptable format, and crops and resizes it appropriately.
func Prepare(data []byte) (*image.Image, error) {
imgCfg, _, err := image.DecodeConfig(bytes.NewReader(data))
// processAvatarImage process the avatar image data, crop and resize it if necessary.
// the returned data could be the original image if no processing is needed.
func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) {
imgCfg, imgType, err := image.DecodeConfig(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("DecodeConfig: %w", err)
return nil, fmt.Errorf("image.DecodeConfig: %w", err)
}
// for safety, only accept known types explicitly
if imgType != "png" && imgType != "jpeg" && imgType != "gif" && imgType != "webp" {
return nil, errors.New("unsupported avatar image type")
}
// do not process image which is too large, it would consume too much memory
if imgCfg.Width > setting.Avatar.MaxWidth {
return nil, fmt.Errorf("Image width is too large: %d > %d", imgCfg.Width, setting.Avatar.MaxWidth)
return nil, fmt.Errorf("image width is too large: %d > %d", imgCfg.Width, setting.Avatar.MaxWidth)
}
if imgCfg.Height > setting.Avatar.MaxHeight {
return nil, fmt.Errorf("Image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight)
return nil, fmt.Errorf("image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight)
}
// If the origin is small enough, just use it, then APNG could be supported,
// otherwise, if the image is processed later, APNG loses animation.
// And one more thing, webp is not fully supported, for animated webp, image.DecodeConfig works but Decode fails.
// So for animated webp, if the uploaded file is smaller than maxOriginSize, it will be used, if it's larger, there will be an error.
if len(data) < int(maxOriginSize) {
return data, nil
}
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("Decode: %w", err)
return nil, fmt.Errorf("image.Decode: %w", err)
}
// try to crop and resize the origin image if necessary
if imgCfg.Width != imgCfg.Height {
var newSize, ax, ay int
if imgCfg.Width > imgCfg.Height {
@@ -74,13 +94,33 @@ func Prepare(data []byte) (*image.Image, error) {
img, err = cutter.Crop(img, cutter.Config{
Width: newSize,
Height: newSize,
Anchor: image.Point{ax, ay},
Anchor: image.Point{X: ax, Y: ay},
})
if err != nil {
return nil, err
}
}
img = resize.Resize(AvatarSize, AvatarSize, img, resize.Bilinear)
return &img, nil
targetSize := uint(DefaultAvatarSize * setting.Avatar.RenderedSizeFactor)
img = resize.Resize(targetSize, targetSize, img, resize.Bilinear)
// try to encode the cropped/resized image to png
bs := bytes.Buffer{}
if err = png.Encode(&bs, img); err != nil {
return nil, err
}
resized := bs.Bytes()
// usually the png compression is not good enough, use the original image (no cropping/resizing) if the origin is smaller
if len(data) <= len(resized) {
return data, nil
}
return resized, nil
}
// ProcessAvatarImage process the avatar image data, crop and resize it if necessary.
// the returned data could be the original image if no processing is needed.
func ProcessAvatarImage(data []byte) ([]byte, error) {
return processAvatarImage(data, setting.Avatar.MaxOriginSize)
}
+79 -16
View File
@@ -4,6 +4,9 @@
package avatar
import (
"bytes"
"image"
"image/png"
"os"
"testing"
@@ -25,49 +28,109 @@ func Test_RandomImage(t *testing.T) {
assert.NoError(t, err)
}
func Test_PrepareWithPNG(t *testing.T) {
func Test_ProcessAvatarPNG(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
data, err := os.ReadFile("testdata/avatar.png")
assert.NoError(t, err)
imgPtr, err := Prepare(data)
_, err = processAvatarImage(data, 262144)
assert.NoError(t, err)
assert.Equal(t, 290, (*imgPtr).Bounds().Max.X)
assert.Equal(t, 290, (*imgPtr).Bounds().Max.Y)
}
func Test_PrepareWithJPEG(t *testing.T) {
func Test_ProcessAvatarJPEG(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
data, err := os.ReadFile("testdata/avatar.jpeg")
assert.NoError(t, err)
imgPtr, err := Prepare(data)
_, err = processAvatarImage(data, 262144)
assert.NoError(t, err)
assert.Equal(t, 290, (*imgPtr).Bounds().Max.X)
assert.Equal(t, 290, (*imgPtr).Bounds().Max.Y)
}
func Test_PrepareWithInvalidImage(t *testing.T) {
func Test_ProcessAvatarInvalidData(t *testing.T) {
setting.Avatar.MaxWidth = 5
setting.Avatar.MaxHeight = 5
_, err := Prepare([]byte{})
assert.EqualError(t, err, "DecodeConfig: image: unknown format")
_, err := processAvatarImage([]byte{}, 12800)
assert.EqualError(t, err, "image.DecodeConfig: image: unknown format")
}
func Test_PrepareWithInvalidImageSize(t *testing.T) {
func Test_ProcessAvatarInvalidImageSize(t *testing.T) {
setting.Avatar.MaxWidth = 5
setting.Avatar.MaxHeight = 5
data, err := os.ReadFile("testdata/avatar.png")
assert.NoError(t, err)
_, err = Prepare(data)
assert.EqualError(t, err, "Image width is too large: 10 > 5")
_, err = processAvatarImage(data, 12800)
assert.EqualError(t, err, "image width is too large: 10 > 5")
}
func Test_ProcessAvatarImage(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
scaledSize := DefaultAvatarSize * setting.Avatar.RenderedSizeFactor
newImgData := func(size int, optHeight ...int) []byte {
width := size
height := size
if len(optHeight) == 1 {
height = optHeight[0]
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
bs := bytes.Buffer{}
err := png.Encode(&bs, img)
assert.NoError(t, err)
return bs.Bytes()
}
// if origin image canvas is too large, crop and resize it
origin := newImgData(500, 600)
result, err := processAvatarImage(origin, 0)
assert.NoError(t, err)
assert.NotEqual(t, origin, result)
decoded, err := png.Decode(bytes.NewReader(result))
assert.NoError(t, err)
assert.EqualValues(t, scaledSize, decoded.Bounds().Max.X)
assert.EqualValues(t, scaledSize, decoded.Bounds().Max.Y)
// if origin image is smaller than the default size, use the origin image
origin = newImgData(1)
result, err = processAvatarImage(origin, 0)
assert.NoError(t, err)
assert.Equal(t, origin, result)
// use the origin image if the origin is smaller
origin = newImgData(scaledSize + 100)
result, err = processAvatarImage(origin, 0)
assert.NoError(t, err)
assert.Less(t, len(result), len(origin))
// still use the origin image if the origin doesn't exceed the max-origin-size
origin = newImgData(scaledSize + 100)
result, err = processAvatarImage(origin, 262144)
assert.NoError(t, err)
assert.Equal(t, origin, result)
// allow to use known image format (eg: webp) if it is small enough
origin, err = os.ReadFile("testdata/animated.webp")
assert.NoError(t, err)
result, err = processAvatarImage(origin, 262144)
assert.NoError(t, err)
assert.Equal(t, origin, result)
// do not support unknown image formats, eg: SVG may contain embedded JS
origin = []byte("<svg></svg>")
_, err = processAvatarImage(origin, 262144)
assert.ErrorContains(t, err, "image: unknown format")
// make sure the canvas size limit works
setting.Avatar.MaxWidth = 5
setting.Avatar.MaxHeight = 5
origin = newImgData(10)
_, err = processAvatarImage(origin, 262144)
assert.ErrorContains(t, err, "image width is too large: 10 > 5")
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

+2 -2
View File
@@ -5,16 +5,16 @@ package context
import (
"net/http"
"net/http/httptest"
"testing"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func TestRemoveSessionCookieHeader(t *testing.T) {
w := httplib.NewMockResponseWriter()
w := httptest.NewRecorder()
w.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String())
w.Header().Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String())
assert.Len(t, w.Header().Values("Set-Cookie"), 2)
+10 -8
View File
@@ -61,7 +61,6 @@ type Repository struct {
RepoLink string
CloneLink repo_model.CloneLink
CommitsCount int64
Mirror *repo_model.Mirror
PullRequest *PullRequest
}
@@ -319,7 +318,14 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
ctx.PlainText(http.StatusBadRequest, "invalid repository path")
return
}
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), repo_model.ComposeHTTPSCloneURL(username, reponame))
var cloneURL string
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
cloneURL = repo_model.ComposeSSHCloneURL(username, reponame)
} else {
cloneURL = repo_model.ComposeHTTPSCloneURL(username, reponame)
}
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), cloneURL)
htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
ctx.PlainText(http.StatusOK, htmlMeta)
}
@@ -373,13 +379,9 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
ctx.Data["Permission"] = &ctx.Repo.Permission
if repo.IsMirror {
ctx.Repo.Mirror, err = repo_model.GetMirrorByRepoID(ctx, repo.ID)
pullMirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
if err == nil {
ctx.Repo.Mirror.Repo = repo
ctx.Data["IsPullMirror"] = true
ctx.Data["MirrorEnablePrune"] = ctx.Repo.Mirror.EnablePrune
ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval
ctx.Data["Mirror"] = ctx.Repo.Mirror
ctx.Data["PullMirror"] = pullMirror
} else if err != repo_model.ErrMirrorNotExist {
ctx.ServerError("GetMirrorByRepoID", err)
return
+11
View File
@@ -106,6 +106,17 @@ func GetBranchesByPath(ctx context.Context, path string, skip, limit int) ([]*Br
return gitRepo.GetBranches(skip, limit)
}
// GetBranchCommitID returns a branch commit ID by its name
func GetBranchCommitID(ctx context.Context, path, branch string) (string, error) {
gitRepo, err := OpenRepository(ctx, path)
if err != nil {
return "", err
}
defer gitRepo.Close()
return gitRepo.GetBranchCommitID(branch)
}
// GetBranches returns a slice of *git.Branch
func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) {
brs, countAll, err := repo.GetBranchNames(skip, limit)
+65 -2
View File
@@ -11,6 +11,7 @@ import (
"os"
"os/signal"
"runtime/pprof"
"strconv"
"sync"
"syscall"
"time"
@@ -45,7 +46,7 @@ type Manager struct {
func newGracefulManager(ctx context.Context) *Manager {
manager := &Manager{
isChild: len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1,
isChild: len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1,
lock: &sync.RWMutex{},
}
manager.createServerWaitGroup.Add(numberOfServersToCreate)
@@ -53,6 +54,41 @@ func newGracefulManager(ctx context.Context) *Manager {
return manager
}
type systemdNotifyMsg string
const (
readyMsg systemdNotifyMsg = "READY=1"
stoppingMsg systemdNotifyMsg = "STOPPING=1"
reloadingMsg systemdNotifyMsg = "RELOADING=1"
watchdogMsg systemdNotifyMsg = "WATCHDOG=1"
)
func statusMsg(msg string) systemdNotifyMsg {
return systemdNotifyMsg("STATUS=" + msg)
}
func pidMsg() systemdNotifyMsg {
return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid()))
}
// Notify systemd of status via the notify protocol
func (g *Manager) notify(msg systemdNotifyMsg) {
conn, err := getNotifySocket()
if err != nil {
// the err is logged in getNotifySocket
return
}
if conn == nil {
return
}
defer conn.Close()
if _, err = conn.Write([]byte(msg)); err != nil {
log.Warn("Failed to notify NOTIFY_SOCKET: %v", err)
return
}
}
func (g *Manager) start(ctx context.Context) {
// Make contexts
g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx)
@@ -72,6 +108,8 @@ func (g *Manager) start(ctx context.Context) {
// Set the running state & handle signals
g.setState(stateRunning)
g.notify(statusMsg("Starting Gitea"))
g.notify(pidMsg())
go g.handleSignals(g.managerCtx)
// Handle clean up of unused provided listeners and delayed start-up
@@ -84,6 +122,7 @@ func (g *Manager) start(ctx context.Context) {
// Ignore the error here there's not much we can do with it
// They're logged in the CloseProvidedListeners function
_ = CloseProvidedListeners()
g.notify(readyMsg)
}()
if setting.StartupTimeout > 0 {
go func() {
@@ -104,6 +143,8 @@ func (g *Manager) start(ctx context.Context) {
return
case <-time.After(setting.StartupTimeout):
log.Error("Startup took too long! Shutting down")
g.notify(statusMsg("Startup took too long! Shutting down"))
g.notify(stoppingMsg)
g.doShutdown()
}
}()
@@ -126,6 +167,13 @@ func (g *Manager) handleSignals(ctx context.Context) {
syscall.SIGTSTP,
)
watchdogTimeout := getWatchdogTimeout()
t := &time.Ticker{}
if watchdogTimeout != 0 {
g.notify(watchdogMsg)
t = time.NewTicker(watchdogTimeout / 2)
}
pid := syscall.Getpid()
for {
select {
@@ -136,6 +184,7 @@ func (g *Manager) handleSignals(ctx context.Context) {
g.DoGracefulRestart()
case syscall.SIGUSR1:
log.Warn("PID %d. Received SIGUSR1. Releasing and reopening logs", pid)
g.notify(statusMsg("Releasing and reopening logs"))
if err := log.ReleaseReopen(); err != nil {
log.Error("Error whilst releasing and reopening logs: %v", err)
}
@@ -153,6 +202,8 @@ func (g *Manager) handleSignals(ctx context.Context) {
default:
log.Info("PID %d. Received %v.", pid, sig)
}
case <-t.C:
g.notify(watchdogMsg)
case <-ctx.Done():
log.Warn("PID: %d. Background context for manager closed - %v - Shutting down...", pid, ctx.Err())
g.DoGracefulShutdown()
@@ -169,6 +220,9 @@ func (g *Manager) doFork() error {
}
g.forked = true
g.lock.Unlock()
g.notify(reloadingMsg)
// We need to move the file logs to append pids
setting.RestartLogsWithPIDSuffix()
@@ -191,18 +245,27 @@ func (g *Manager) DoGracefulRestart() {
}
} else {
log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid())
g.notify(stoppingMsg)
g.doShutdown()
}
}
// DoImmediateHammer causes an immediate hammer
func (g *Manager) DoImmediateHammer() {
g.notify(statusMsg("Sending immediate hammer"))
g.doHammerTime(0 * time.Second)
}
// DoGracefulShutdown causes a graceful shutdown
func (g *Manager) DoGracefulShutdown() {
g.lock.Lock()
if !g.forked {
g.lock.Unlock()
g.notify(stoppingMsg)
} else {
g.lock.Unlock()
g.notify(statusMsg("Shutting down after fork"))
}
g.doShutdown()
}
+81 -7
View File
@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"sync"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -21,9 +22,12 @@ import (
)
const (
listenFDs = "LISTEN_FDS"
startFD = 3
unlinkFDs = "GITEA_UNLINK_FDS"
listenFDsEnv = "LISTEN_FDS"
startFD = 3
unlinkFDsEnv = "GITEA_UNLINK_FDS"
notifySocketEnv = "NOTIFY_SOCKET"
watchdogTimeoutEnv = "WATCHDOG_USEC"
)
// In order to keep the working directory the same as when we started we record
@@ -38,6 +42,9 @@ var (
activeListenersToUnlink = []bool{}
providedListeners = []net.Listener{}
activeListeners = []net.Listener{}
notifySocketAddr string
watchdogTimeout time.Duration
)
func getProvidedFDs() (savedErr error) {
@@ -45,18 +52,52 @@ func getProvidedFDs() (savedErr error) {
once.Do(func() {
mutex.Lock()
defer mutex.Unlock()
// now handle some additional systemd provided things
notifySocketAddr = os.Getenv(notifySocketEnv)
if notifySocketAddr != "" {
log.Debug("Systemd Notify Socket provided: %s", notifySocketAddr)
savedErr = os.Unsetenv(notifySocketEnv)
if savedErr != nil {
log.Warn("Unable to Unset the NOTIFY_SOCKET environment variable: %v", savedErr)
return
}
// FIXME: We don't handle WATCHDOG_PID
timeoutStr := os.Getenv(watchdogTimeoutEnv)
if timeoutStr != "" {
savedErr = os.Unsetenv(watchdogTimeoutEnv)
if savedErr != nil {
log.Warn("Unable to Unset the WATCHDOG_USEC environment variable: %v", savedErr)
return
}
numFDs := os.Getenv(listenFDs)
s, err := strconv.ParseInt(timeoutStr, 10, 64)
if err != nil {
log.Error("Unable to parse the provided WATCHDOG_USEC: %v", err)
savedErr = fmt.Errorf("unable to parse the provided WATCHDOG_USEC: %w", err)
return
}
if s <= 0 {
log.Error("Unable to parse the provided WATCHDOG_USEC: %s should be a positive number", timeoutStr)
savedErr = fmt.Errorf("unable to parse the provided WATCHDOG_USEC: %s should be a positive number", timeoutStr)
return
}
watchdogTimeout = time.Duration(s) * time.Microsecond
}
} else {
log.Trace("No Systemd Notify Socket provided")
}
numFDs := os.Getenv(listenFDsEnv)
if numFDs == "" {
return
}
n, err := strconv.Atoi(numFDs)
if err != nil {
savedErr = fmt.Errorf("%s is not a number: %s. Err: %w", listenFDs, numFDs, err)
savedErr = fmt.Errorf("%s is not a number: %s. Err: %w", listenFDsEnv, numFDs, err)
return
}
fdsToUnlinkStr := strings.Split(os.Getenv(unlinkFDs), ",")
fdsToUnlinkStr := strings.Split(os.Getenv(unlinkFDsEnv), ",")
providedListenersToUnlink = make([]bool, n)
for _, fdStr := range fdsToUnlinkStr {
i, err := strconv.Atoi(fdStr)
@@ -73,7 +114,7 @@ func getProvidedFDs() (savedErr error) {
if err == nil {
// Close the inherited file if it's a listener
if err = file.Close(); err != nil {
savedErr = fmt.Errorf("error closing provided socket fd %d: %s", i, err)
savedErr = fmt.Errorf("error closing provided socket fd %d: %w", i, err)
return
}
providedListeners = append(providedListeners, l)
@@ -255,3 +296,36 @@ func getActiveListenersToUnlink() []bool {
copy(listenersToUnlink, activeListenersToUnlink)
return listenersToUnlink
}
func getNotifySocket() (*net.UnixConn, error) {
if err := getProvidedFDs(); err != nil {
// This error will be logged elsewhere
return nil, nil
}
if notifySocketAddr == "" {
return nil, nil
}
socketAddr := &net.UnixAddr{
Name: notifySocketAddr,
Net: "unixgram",
}
notifySocket, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
if err != nil {
log.Warn("failed to dial NOTIFY_SOCKET %s: %v", socketAddr, err)
return nil, err
}
return notifySocket, nil
}
func getWatchdogTimeout() time.Duration {
if err := getProvidedFDs(); err != nil {
// This error will be logged elsewhere
return 0
}
return watchdogTimeout
}
+13 -3
View File
@@ -16,6 +16,7 @@ import (
"strings"
"sync"
"syscall"
"time"
)
var killParent sync.Once
@@ -70,11 +71,20 @@ func RestartProcess() (int, error) {
// Pass on the environment and replace the old count key with the new one.
var env []string
for _, v := range os.Environ() {
if !strings.HasPrefix(v, listenFDs+"=") {
if !strings.HasPrefix(v, listenFDsEnv+"=") {
env = append(env, v)
}
}
env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners)))
env = append(env, fmt.Sprintf("%s=%d", listenFDsEnv, len(listeners)))
if notifySocketAddr != "" {
env = append(env, fmt.Sprintf("%s=%s", notifySocketEnv, notifySocketAddr))
}
if watchdogTimeout != 0 {
watchdogStr := strconv.FormatInt(int64(watchdogTimeout/time.Millisecond), 10)
env = append(env, fmt.Sprintf("%s=%s", watchdogTimeoutEnv, watchdogStr))
}
sb := &strings.Builder{}
for i, unlink := range getActiveListenersToUnlink() {
@@ -87,7 +97,7 @@ func RestartProcess() (int, error) {
unlinkStr := sb.String()
if len(unlinkStr) > 0 {
unlinkStr = unlinkStr[:len(unlinkStr)-1]
env = append(env, fmt.Sprintf("%s=%s", unlinkFDs, unlinkStr))
env = append(env, fmt.Sprintf("%s=%s", unlinkFDsEnv, unlinkStr))
}
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
+3 -34
View File
@@ -4,10 +4,8 @@
package httpcache
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
@@ -37,38 +35,9 @@ func SetCacheControlInHeader(h http.Header, maxAge time.Duration, additionalDire
h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
}
// generateETag generates an ETag based on size, filename and file modification time
func generateETag(fi os.FileInfo) string {
etag := fmt.Sprint(fi.Size()) + fi.Name() + fi.ModTime().UTC().Format(http.TimeFormat)
return `"` + base64.StdEncoding.EncodeToString([]byte(etag)) + `"`
}
// HandleTimeCache handles time-based caching for a HTTP request
func HandleTimeCache(req *http.Request, w http.ResponseWriter, fi os.FileInfo) (handled bool) {
return HandleGenericTimeCache(req, w, fi.ModTime())
}
// HandleGenericTimeCache handles time-based caching for a HTTP request
func HandleGenericTimeCache(req *http.Request, w http.ResponseWriter, lastModified time.Time) (handled bool) {
func ServeContentWithCacheControl(w http.ResponseWriter, req *http.Request, name string, modTime time.Time, content io.ReadSeeker) {
SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
ifModifiedSince := req.Header.Get("If-Modified-Since")
if ifModifiedSince != "" {
t, err := time.Parse(http.TimeFormat, ifModifiedSince)
if err == nil && lastModified.Unix() <= t.Unix() {
w.WriteHeader(http.StatusNotModified)
return true
}
}
w.Header().Set("Last-Modified", lastModified.Format(http.TimeFormat))
return false
}
// HandleFileETagCache handles ETag-based caching for a HTTP request
func HandleFileETagCache(req *http.Request, w http.ResponseWriter, fi os.FileInfo) (handled bool) {
etag := generateETag(fi)
return HandleGenericETagCache(req, w, etag)
http.ServeContent(w, req, name, modTime, content)
}
// HandleGenericETagCache handles ETag-based caching for a HTTP request.
-57
View File
@@ -6,23 +6,12 @@ package httpcache
import (
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
type mockFileInfo struct{}
func (m mockFileInfo) Name() string { return "gitea.test" }
func (m mockFileInfo) Size() int64 { return int64(10) }
func (m mockFileInfo) Mode() os.FileMode { return os.ModePerm }
func (m mockFileInfo) ModTime() time.Time { return time.Time{} }
func (m mockFileInfo) IsDir() bool { return false }
func (m mockFileInfo) Sys() interface{} { return nil }
func countFormalHeaders(h http.Header) (c int) {
for k := range h {
// ignore our headers for internal usage
@@ -34,52 +23,6 @@ func countFormalHeaders(h http.Header) (c int) {
return c
}
func TestHandleFileETagCache(t *testing.T) {
fi := mockFileInfo{}
etag := `"MTBnaXRlYS50ZXN0TW9uLCAwMSBKYW4gMDAwMSAwMDowMDowMCBHTVQ="`
t.Run("No_If-None-Match", func(t *testing.T) {
req := &http.Request{Header: make(http.Header)}
w := httptest.NewRecorder()
handled := HandleFileETagCache(req, w, fi)
assert.False(t, handled)
assert.Equal(t, 2, countFormalHeaders(w.Header()))
assert.Contains(t, w.Header(), "Cache-Control")
assert.Contains(t, w.Header(), "Etag")
assert.Equal(t, etag, w.Header().Get("Etag"))
})
t.Run("Wrong_If-None-Match", func(t *testing.T) {
req := &http.Request{Header: make(http.Header)}
w := httptest.NewRecorder()
req.Header.Set("If-None-Match", `"wrong etag"`)
handled := HandleFileETagCache(req, w, fi)
assert.False(t, handled)
assert.Equal(t, 2, countFormalHeaders(w.Header()))
assert.Contains(t, w.Header(), "Cache-Control")
assert.Contains(t, w.Header(), "Etag")
assert.Equal(t, etag, w.Header().Get("Etag"))
})
t.Run("Correct_If-None-Match", func(t *testing.T) {
req := &http.Request{Header: make(http.Header)}
w := httptest.NewRecorder()
req.Header.Set("If-None-Match", etag)
handled := HandleFileETagCache(req, w, fi)
assert.True(t, handled)
assert.Equal(t, 1, countFormalHeaders(w.Header()))
assert.Contains(t, w.Header(), "Etag")
assert.Equal(t, etag, w.Header().Get("Etag"))
assert.Equal(t, http.StatusNotModified, w.Code)
})
}
func TestHandleGenericETagCache(t *testing.T) {
etag := `"test"`
-35
View File
@@ -1,35 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package httplib
import (
"bytes"
"net/http"
)
type MockResponseWriter struct {
header http.Header
StatusCode int
BodyBuffer bytes.Buffer
}
func (m *MockResponseWriter) Header() http.Header {
return m.header
}
func (m *MockResponseWriter) Write(bytes []byte) (int, error) {
if m.StatusCode == 0 {
m.StatusCode = http.StatusOK
}
return m.BodyBuffer.Write(bytes)
}
func (m *MockResponseWriter) WriteHeader(statusCode int) {
m.StatusCode = statusCode
}
func NewMockResponseWriter() *MockResponseWriter {
return &MockResponseWriter{header: http.Header{}}
}
+7 -6
View File
@@ -6,6 +6,7 @@ package httplib
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
@@ -25,12 +26,12 @@ func TestServeContentByReader(t *testing.T) {
r.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeStr))
}
reader := strings.NewReader(data)
w := NewMockResponseWriter()
w := httptest.NewRecorder()
ServeContentByReader(r, w, "test", int64(len(data)), reader)
assert.Equal(t, expectedStatusCode, w.StatusCode)
assert.Equal(t, expectedStatusCode, w.Code)
if expectedStatusCode == http.StatusPartialContent || expectedStatusCode == http.StatusOK {
assert.Equal(t, fmt.Sprint(len(expectedContent)), w.Header().Get("Content-Length"))
assert.Equal(t, expectedContent, w.BodyBuffer.String())
assert.Equal(t, expectedContent, w.Body.String())
}
}
@@ -76,12 +77,12 @@ func TestServeContentByReadSeeker(t *testing.T) {
}
defer seekReader.Close()
w := NewMockResponseWriter()
w := httptest.NewRecorder()
ServeContentByReadSeeker(r, w, "test", time.Time{}, seekReader)
assert.Equal(t, expectedStatusCode, w.StatusCode)
assert.Equal(t, expectedStatusCode, w.Code)
if expectedStatusCode == http.StatusPartialContent || expectedStatusCode == http.StatusOK {
assert.Equal(t, fmt.Sprint(len(expectedContent)), w.Header().Get("Content-Length"))
assert.Equal(t, expectedContent, w.BodyBuffer.String())
assert.Equal(t, expectedContent, w.Body.String())
}
}
+236
View File
@@ -0,0 +1,236 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package alpine
import (
"archive/tar"
"bufio"
"compress/gzip"
"crypto/sha1"
"encoding/base64"
"io"
"strconv"
"strings"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
)
var (
ErrMissingPKGINFOFile = util.NewInvalidArgumentErrorf("PKGINFO file is missing")
ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
)
const (
PropertyMetadata = "alpine.metadata"
PropertyBranch = "alpine.branch"
PropertyRepository = "alpine.repository"
PropertyArchitecture = "alpine.architecture"
SettingKeyPrivate = "alpine.key.private"
SettingKeyPublic = "alpine.key.public"
RepositoryPackage = "_alpine"
RepositoryVersion = "_repository"
)
// https://wiki.alpinelinux.org/wiki/Apk_spec
// Package represents an Alpine package
type Package struct {
Name string
Version string
VersionMetadata VersionMetadata
FileMetadata FileMetadata
}
// Metadata of an Alpine package
type VersionMetadata struct {
Description string `json:"description,omitempty"`
License string `json:"license,omitempty"`
ProjectURL string `json:"project_url,omitempty"`
Maintainer string `json:"maintainer,omitempty"`
}
type FileMetadata struct {
Checksum string `json:"checksum"`
Packager string `json:"packager,omitempty"`
BuildDate int64 `json:"build_date,omitempty"`
Size int64 `json:"size,omitempty"`
Architecture string `json:"architecture,omitempty"`
Origin string `json:"origin,omitempty"`
CommitHash string `json:"commit_hash,omitempty"`
InstallIf string `json:"install_if,omitempty"`
Provides []string `json:"provides,omitempty"`
Dependencies []string `json:"dependencies,omitempty"`
}
// ParsePackage parses the Alpine package file
func ParsePackage(r io.Reader) (*Package, error) {
// Alpine packages are concated .tar.gz streams. Usually the first stream contains the package metadata.
br := bufio.NewReader(r) // needed for gzip Multistream
h := sha1.New()
gzr, err := gzip.NewReader(&teeByteReader{br, h})
if err != nil {
return nil, err
}
defer gzr.Close()
for {
gzr.Multistream(false)
tr := tar.NewReader(gzr)
for {
hd, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if hd.Name == ".PKGINFO" {
p, err := ParsePackageInfo(tr)
if err != nil {
return nil, err
}
// drain the reader
for {
if _, err := tr.Next(); err != nil {
break
}
}
p.FileMetadata.Checksum = "Q1" + base64.StdEncoding.EncodeToString(h.Sum(nil))
return p, nil
}
}
h = sha1.New()
err = gzr.Reset(&teeByteReader{br, h})
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
}
return nil, ErrMissingPKGINFOFile
}
// ParsePackageInfo parses a PKGINFO file to retrieve the metadata of an Alpine package
func ParsePackageInfo(r io.Reader) (*Package, error) {
p := &Package{}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "#") {
continue
}
i := strings.IndexRune(line, '=')
if i == -1 {
continue
}
key := strings.TrimSpace(line[:i])
value := strings.TrimSpace(line[i+1:])
switch key {
case "pkgname":
p.Name = value
case "pkgver":
p.Version = value
case "pkgdesc":
p.VersionMetadata.Description = value
case "url":
p.VersionMetadata.ProjectURL = value
case "builddate":
n, err := strconv.ParseInt(value, 10, 64)
if err == nil {
p.FileMetadata.BuildDate = n
}
case "size":
n, err := strconv.ParseInt(value, 10, 64)
if err == nil {
p.FileMetadata.Size = n
}
case "arch":
p.FileMetadata.Architecture = value
case "origin":
p.FileMetadata.Origin = value
case "commit":
p.FileMetadata.CommitHash = value
case "maintainer":
p.VersionMetadata.Maintainer = value
case "packager":
p.FileMetadata.Packager = value
case "license":
p.VersionMetadata.License = value
case "install_if":
p.FileMetadata.InstallIf = value
case "provides":
if value != "" {
p.FileMetadata.Provides = append(p.FileMetadata.Provides, value)
}
case "depend":
if value != "" {
p.FileMetadata.Dependencies = append(p.FileMetadata.Dependencies, value)
}
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
if p.Name == "" {
return nil, ErrInvalidName
}
if p.Version == "" {
return nil, ErrInvalidVersion
}
if !validation.IsValidURL(p.VersionMetadata.ProjectURL) {
p.VersionMetadata.ProjectURL = ""
}
return p, nil
}
// Same as io.TeeReader but implements io.ByteReader
type teeByteReader struct {
r *bufio.Reader
w io.Writer
}
func (t *teeByteReader) Read(p []byte) (int, error) {
n, err := t.r.Read(p)
if n > 0 {
if n, err := t.w.Write(p[:n]); err != nil {
return n, err
}
}
return n, err
}
func (t *teeByteReader) ReadByte() (byte, error) {
b, err := t.r.ReadByte()
if err == nil {
if _, err := t.w.Write([]byte{b}); err != nil {
return 0, err
}
}
return b, err
}
+143
View File
@@ -0,0 +1,143 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package alpine
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"testing"
"github.com/stretchr/testify/assert"
)
const (
packageName = "gitea"
packageVersion = "1.0.1"
packageDescription = "Package Description"
packageProjectURL = "https://gitea.io"
packageMaintainer = "KN4CK3R <dummy@gitea.io>"
)
func createPKGINFOContent(name, version string) []byte {
return []byte(`pkgname = ` + name + `
pkgver = ` + version + `
pkgdesc = ` + packageDescription + `
url = ` + packageProjectURL + `
# comment
builddate = 1678834800
packager = Gitea <pack@ag.er>
size = 123456
arch = aarch64
origin = origin
commit = 1111e709613fbc979651b09ac2bc27c6591a9999
maintainer = ` + packageMaintainer + `
license = MIT
depend = common
install_if = value
depend = gitea
provides = common
provides = gitea`)
}
func TestParsePackage(t *testing.T) {
createPackage := func(name string, content []byte) io.Reader {
names := []string{"first.stream", name}
contents := [][]byte{{0}, content}
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
for i := range names {
if i != 0 {
zw.Close()
zw.Reset(&buf)
}
tw := tar.NewWriter(zw)
hdr := &tar.Header{
Name: names[i],
Mode: 0o600,
Size: int64(len(contents[i])),
}
tw.WriteHeader(hdr)
tw.Write(contents[i])
tw.Close()
}
zw.Close()
return &buf
}
t.Run("MissingPKGINFOFile", func(t *testing.T) {
data := createPackage("dummy.txt", []byte{})
pp, err := ParsePackage(data)
assert.Nil(t, pp)
assert.ErrorIs(t, err, ErrMissingPKGINFOFile)
})
t.Run("InvalidPKGINFOFile", func(t *testing.T) {
data := createPackage(".PKGINFO", []byte{})
pp, err := ParsePackage(data)
assert.Nil(t, pp)
assert.ErrorIs(t, err, ErrInvalidName)
})
t.Run("Valid", func(t *testing.T) {
data := createPackage(".PKGINFO", createPKGINFOContent(packageName, packageVersion))
p, err := ParsePackage(data)
assert.NoError(t, err)
assert.NotNil(t, p)
assert.Equal(t, "Q1SRYURM5+uQDqfHSwTnNIOIuuDVQ=", p.FileMetadata.Checksum)
})
}
func TestParsePackageInfo(t *testing.T) {
t.Run("InvalidName", func(t *testing.T) {
data := createPKGINFOContent("", packageVersion)
p, err := ParsePackageInfo(bytes.NewReader(data))
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrInvalidName)
})
t.Run("InvalidVersion", func(t *testing.T) {
data := createPKGINFOContent(packageName, "")
p, err := ParsePackageInfo(bytes.NewReader(data))
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrInvalidVersion)
})
t.Run("Valid", func(t *testing.T) {
data := createPKGINFOContent(packageName, packageVersion)
p, err := ParsePackageInfo(bytes.NewReader(data))
assert.NoError(t, err)
assert.NotNil(t, p)
assert.Equal(t, packageName, p.Name)
assert.Equal(t, packageVersion, p.Version)
assert.Equal(t, packageDescription, p.VersionMetadata.Description)
assert.Equal(t, packageMaintainer, p.VersionMetadata.Maintainer)
assert.Equal(t, packageProjectURL, p.VersionMetadata.ProjectURL)
assert.Equal(t, "MIT", p.VersionMetadata.License)
assert.Empty(t, p.FileMetadata.Checksum)
assert.Equal(t, "Gitea <pack@ag.er>", p.FileMetadata.Packager)
assert.EqualValues(t, 1678834800, p.FileMetadata.BuildDate)
assert.EqualValues(t, 123456, p.FileMetadata.Size)
assert.Equal(t, "aarch64", p.FileMetadata.Architecture)
assert.Equal(t, "origin", p.FileMetadata.Origin)
assert.Equal(t, "1111e709613fbc979651b09ac2bc27c6591a9999", p.FileMetadata.CommitHash)
assert.Equal(t, "value", p.FileMetadata.InstallIf)
assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Provides)
assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Dependencies)
})
}
+94
View File
@@ -0,0 +1,94 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package goproxy
import (
"archive/zip"
"fmt"
"io"
"path"
"strings"
"code.gitea.io/gitea/modules/util"
)
const (
PropertyGoMod = "go.mod"
maxGoModFileSize = 16 * 1024 * 1024 // https://go.dev/ref/mod#zip-path-size-constraints
)
var (
ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure")
ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large")
)
type Package struct {
Name string
Version string
GoMod string
}
// ParsePackage parses the Go package file
// https://go.dev/ref/mod#zip-files
func ParsePackage(r io.ReaderAt, size int64) (*Package, error) {
archive, err := zip.NewReader(r, size)
if err != nil {
return nil, err
}
var p *Package
for _, file := range archive.File {
nameAndVersion := path.Dir(file.Name)
parts := strings.SplitN(nameAndVersion, "@", 2)
if len(parts) != 2 {
continue
}
versionParts := strings.SplitN(parts[1], "/", 2)
if p == nil {
p = &Package{
Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]),
Version: versionParts[0],
}
}
if len(versionParts) > 1 {
// files are expected in the "root" folder
continue
}
if path.Base(file.Name) == "go.mod" {
if file.UncompressedSize64 > maxGoModFileSize {
return nil, ErrGoModFileTooLarge
}
f, err := archive.Open(file.Name)
if err != nil {
return nil, err
}
defer f.Close()
bytes, err := io.ReadAll(&io.LimitedReader{R: f, N: maxGoModFileSize})
if err != nil {
return nil, err
}
p.GoMod = string(bytes)
return p, nil
}
}
if p == nil {
return nil, ErrInvalidStructure
}
p.GoMod = fmt.Sprintf("module %s", p.Name)
return p, nil
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package goproxy
import (
"archive/zip"
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
const (
packageName = "gitea.com/go-gitea/gitea"
packageVersion = "v0.0.1"
)
func TestParsePackage(t *testing.T) {
createArchive := func(files map[string][]byte) *bytes.Reader {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
for name, content := range files {
w, _ := zw.Create(name)
w.Write(content)
}
zw.Close()
return bytes.NewReader(buf.Bytes())
}
t.Run("EmptyPackage", func(t *testing.T) {
data := createArchive(nil)
p, err := ParsePackage(data, int64(data.Len()))
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrInvalidStructure)
})
t.Run("InvalidNameOrVersionStructure", func(t *testing.T) {
data := createArchive(map[string][]byte{
packageName + "/" + packageVersion + "/go.mod": {},
})
p, err := ParsePackage(data, int64(data.Len()))
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrInvalidStructure)
})
t.Run("GoModFileInWrongDirectory", func(t *testing.T) {
data := createArchive(map[string][]byte{
packageName + "@" + packageVersion + "/subdir/go.mod": {},
})
p, err := ParsePackage(data, int64(data.Len()))
assert.NotNil(t, p)
assert.NoError(t, err)
assert.Equal(t, packageName, p.Name)
assert.Equal(t, packageVersion, p.Version)
assert.Equal(t, "module gitea.com/go-gitea/gitea", p.GoMod)
})
t.Run("Valid", func(t *testing.T) {
data := createArchive(map[string][]byte{
packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"),
packageName + "@" + packageVersion + "/go.mod": []byte("valid"),
})
p, err := ParsePackage(data, int64(data.Len()))
assert.NotNil(t, p)
assert.NoError(t, err)
assert.Equal(t, packageName, p.Name)
assert.Equal(t, packageVersion, p.Version)
assert.Equal(t, "valid", p.GoMod)
})
}
+2 -6
View File
@@ -97,10 +97,6 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem,
return true
}
if httpcache.HandleFileETagCache(req, w, fi) {
return true
}
serveContent(w, req, fi, fi.ModTime(), f)
return true
}
@@ -124,11 +120,11 @@ func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modt
w.Header().Set("Content-Type", "application/octet-stream")
}
w.Header().Set("Content-Encoding", "gzip")
http.ServeContent(w, req, fi.Name(), modtime, rdGzip)
httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, rdGzip)
return
}
}
http.ServeContent(w, req, fi.Name(), modtime, content)
httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, content)
return
}
+3 -4
View File
@@ -6,6 +6,7 @@ package repository
import (
"crypto/md5"
"fmt"
"strconv"
"testing"
"time"
@@ -136,13 +137,11 @@ func TestPushCommits_AvatarLink(t *testing.T) {
enableGravatar(t)
assert.Equal(t,
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84",
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor),
pushCommits.AvatarLink(db.DefaultContext, "user2@example.com"))
assert.Equal(t,
"https://secure.gravatar.com/avatar/"+
fmt.Sprintf("%x", md5.Sum([]byte("nonexistent@example.com")))+
"?d=identicon&s=84",
fmt.Sprintf("https://secure.gravatar.com/avatar/%x?d=identicon&s=%d", md5.Sum([]byte("nonexistent@example.com")), 28*setting.Avatar.RenderedSizeFactor),
pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com"))
}
+9 -7
View File
@@ -4,6 +4,7 @@
package setting
import (
"context"
"net"
"net/mail"
"strings"
@@ -198,7 +199,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
ips := tryResolveAddr(MailService.SMTPAddr)
if MailService.Protocol == "smtp" {
for _, ip := range ips {
if !ip.IsLoopback() {
if !ip.IP.IsLoopback() {
log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended")
break
}
@@ -258,20 +259,21 @@ func loadNotifyMailFrom(rootCfg ConfigProvider) {
log.Info("Notify Mail Service Enabled")
}
func tryResolveAddr(addr string) []net.IP {
func tryResolveAddr(addr string) []net.IPAddr {
if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") {
addr = addr[1 : len(addr)-1]
}
ip := net.ParseIP(addr)
if ip != nil {
ips := make([]net.IP, 1)
ips[0] = ip
return ips
return []net.IPAddr{{IP: ip}}
}
ips, err := net.LookupIP(addr)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
ips, err := net.DefaultResolver.LookupIPAddr(ctx, addr)
if err != nil {
log.Warn("could not look up mailer.SMTP_ADDR: %v", err)
return make([]net.IP, 0)
return nil
}
return ips
}
+4
View File
@@ -24,6 +24,7 @@ var (
LimitTotalOwnerCount int64
LimitTotalOwnerSize int64
LimitSizeAlpine int64
LimitSizeCargo int64
LimitSizeChef int64
LimitSizeComposer int64
@@ -32,6 +33,7 @@ var (
LimitSizeContainer int64
LimitSizeDebian int64
LimitSizeGeneric int64
LimitSizeGo int64
LimitSizeHelm int64
LimitSizeMaven int64
LimitSizeNpm int64
@@ -69,6 +71,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
}
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
Packages.LimitSizeAlpine = mustBytes(sec, "LIMIT_SIZE_ALPINE")
Packages.LimitSizeCargo = mustBytes(sec, "LIMIT_SIZE_CARGO")
Packages.LimitSizeChef = mustBytes(sec, "LIMIT_SIZE_CHEF")
Packages.LimitSizeComposer = mustBytes(sec, "LIMIT_SIZE_COMPOSER")
@@ -77,6 +80,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
Packages.LimitSizeContainer = mustBytes(sec, "LIMIT_SIZE_CONTAINER")
Packages.LimitSizeDebian = mustBytes(sec, "LIMIT_SIZE_DEBIAN")
Packages.LimitSizeGeneric = mustBytes(sec, "LIMIT_SIZE_GENERIC")
Packages.LimitSizeGo = mustBytes(sec, "LIMIT_SIZE_GO")
Packages.LimitSizeHelm = mustBytes(sec, "LIMIT_SIZE_HELM")
Packages.LimitSizeMaven = mustBytes(sec, "LIMIT_SIZE_MAVEN")
Packages.LimitSizeNpm = mustBytes(sec, "LIMIT_SIZE_NPM")
+10 -7
View File
@@ -3,21 +3,23 @@
package setting
// settings
// Avatar settings
var (
// Picture settings
Avatar = struct {
Storage
MaxWidth int
MaxHeight int
MaxFileSize int64
MaxOriginSize int64
RenderedSizeFactor int
}{
MaxWidth: 4096,
MaxHeight: 3072,
MaxHeight: 4096,
MaxFileSize: 1048576,
RenderedSizeFactor: 3,
MaxOriginSize: 262144,
RenderedSizeFactor: 2,
}
GravatarSource string
@@ -44,9 +46,10 @@ func loadPictureFrom(rootCfg ConfigProvider) {
Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec)
Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(4096)
Avatar.MaxFileSize = sec.Key("AVATAR_MAX_FILE_SIZE").MustInt64(1048576)
Avatar.RenderedSizeFactor = sec.Key("AVATAR_RENDERED_SIZE_FACTOR").MustInt(3)
Avatar.MaxOriginSize = sec.Key("AVATAR_MAX_ORIGIN_SIZE").MustInt64(262144)
Avatar.RenderedSizeFactor = sec.Key("AVATAR_RENDERED_SIZE_FACTOR").MustInt(2)
switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source {
case "duoshuo":
@@ -94,5 +97,5 @@ func loadRepoAvatarFrom(rootCfg ConfigProvider) {
RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec)
RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png")
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString(AppSubURL + "/assets/img/repo_default.png")
}
+2
View File
@@ -38,6 +38,7 @@ var (
DisableHTTPGit bool
AccessControlAllowOrigin string
UseCompatSSHURI bool
GoGetCloneURLProtocol string
DefaultCloseIssuesViaCommitsInAnyBranch bool
EnablePushCreateUser bool
EnablePushCreateOrg bool
@@ -307,6 +308,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool()
Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool()
Repository.GoGetCloneURLProtocol = sec.Key("GO_GET_CLONE_URL_PROTOCOL").MustString("https")
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
Repository.DefaultBranch = sec.Key("DEFAULT_BRANCH").MustString(Repository.DefaultBranch)
RepoRootPath = sec.Key("ROOT").MustString(path.Join(AppDataPath, "gitea-repositories"))
+2 -2
View File
@@ -133,8 +133,8 @@ func (l *LocalStorage) URL(path, name string) (*url.URL, error) {
}
// IterateObjects iterates across the objects in the local storage
func (l *LocalStorage) IterateObjects(prefix string, fn func(path string, obj Object) error) error {
dir := l.buildLocalPath(prefix)
func (l *LocalStorage) IterateObjects(dirName string, fn func(path string, obj Object) error) error {
dir := l.buildLocalPath(dirName)
return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
+1 -36
View File
@@ -4,8 +4,6 @@
package storage
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"
@@ -57,38 +55,5 @@ func TestBuildLocalPath(t *testing.T) {
func TestLocalStorageIterator(t *testing.T) {
dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir")
l, err := NewLocalStorage(context.Background(), LocalStorageConfig{Path: dir})
assert.NoError(t, err)
testFiles := [][]string{
{"a/1.txt", "a1"},
{"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim
{"b/1.txt", "b1"},
{"b/2.txt", "b2"},
{"b/3.txt", "b3"},
{"b/x 4.txt", "bx4"},
}
for _, f := range testFiles {
_, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1)
assert.NoError(t, err)
}
expectedList := map[string][]string{
"a": {"a/1.txt"},
"b": {"b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"/": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"a/b/../../a": {"a/1.txt"},
}
for dir, expected := range expectedList {
count := 0
err = l.IterateObjects(dir, func(path string, f Object) error {
defer f.Close()
assert.Contains(t, expected, path)
count++
return nil
})
assert.NoError(t, err)
assert.Len(t, expected, count)
}
testStorageIterator(t, string(LocalStorageType), LocalStorageConfig{Path: dir})
}
+10 -5
View File
@@ -129,7 +129,11 @@ func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
}
func (m *MinioStorage) buildMinioPath(p string) string {
return util.PathJoinRelX(m.basePath, p)
p = util.PathJoinRelX(m.basePath, p)
if p == "." {
p = "" // minio doesn't use dot as relative path
}
return p
}
// Open opens a file
@@ -224,14 +228,15 @@ func (m *MinioStorage) URL(path, name string) (*url.URL, error) {
}
// IterateObjects iterates across the objects in the miniostorage
func (m *MinioStorage) IterateObjects(prefix string, fn func(path string, obj Object) error) error {
func (m *MinioStorage) IterateObjects(dirName string, fn func(path string, obj Object) error) error {
opts := minio.GetObjectOptions{}
lobjectCtx, cancel := context.WithCancel(m.ctx)
defer cancel()
basePath := m.basePath
if prefix != "" {
basePath = m.buildMinioPath(prefix)
if dirName != "" {
// ending slash is required for avoiding matching like "foo/" and "foobar/" with prefix "foo"
basePath = m.buildMinioPath(dirName) + "/"
}
for mObjInfo := range m.client.ListObjects(lobjectCtx, m.bucket, minio.ListObjectsOptions{
@@ -244,7 +249,7 @@ func (m *MinioStorage) IterateObjects(prefix string, fn func(path string, obj Ob
}
if err := func(object *minio.Object, fn func(path string, obj Object) error) error {
defer object.Close()
return fn(strings.TrimPrefix(mObjInfo.Key, basePath), &minioObject{object})
return fn(strings.TrimPrefix(mObjInfo.Key, m.basePath), &minioObject{object})
}(object, fn); err != nil {
return convertMinioErr(err)
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package storage
import (
"testing"
)
func TestMinioStorageIterator(t *testing.T) {
testStorageIterator(t, string(MinioStorageType), MinioStorageConfig{
Endpoint: "127.0.0.1:9000",
AccessKeyID: "123456",
SecretAccessKey: "12345678",
Bucket: "gitea",
Location: "us-east-1",
})
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package storage
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func testStorageIterator(t *testing.T, typStr string, cfg interface{}) {
l, err := NewStorage(typStr, cfg)
assert.NoError(t, err)
testFiles := [][]string{
{"a/1.txt", "a1"},
{"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim
{"ab/1.txt", "ab1"},
{"b/1.txt", "b1"},
{"b/2.txt", "b2"},
{"b/3.txt", "b3"},
{"b/x 4.txt", "bx4"},
}
for _, f := range testFiles {
_, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1)
assert.NoError(t, err)
}
expectedList := map[string][]string{
"a": {"a/1.txt"},
"b": {"b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt", "ab/1.txt"},
"/": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt", "ab/1.txt"},
"a/b/../../a": {"a/1.txt"},
}
for dir, expected := range expectedList {
count := 0
err = l.IterateObjects(dir, func(path string, f Object) error {
defer f.Close()
assert.Contains(t, expected, path)
count++
return nil
})
assert.NoError(t, err)
assert.Len(t, expected, count)
}
}
+3 -1
View File
@@ -58,7 +58,9 @@ func (w *testLoggerWriterCloser) Write(p []byte) (int, error) {
}
if t == nil || *t == nil {
return fmt.Fprintf(os.Stdout, "??? [Unknown Test] %s\n", p)
// if there is no running test, the log message should be outputted to console, to avoid losing important information.
// the "???" prefix is used to match the "===" and "+++" in PrintCurrentTest
return fmt.Fprintf(os.Stdout, "??? [TestLogger] %s\n", p)
}
defer func() {
+16
View File
@@ -4,10 +4,13 @@
package util
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"github.com/minio/sha256-simd"
)
// GenerateKeyPair generates a public and private keypair
@@ -43,3 +46,16 @@ func pemBlockForPub(pub *rsa.PublicKey) (string, error) {
})
return string(pubBytes), nil
}
// CreatePublicKeyFingerprint creates a fingerprint of the given key.
// The fingerprint is the sha256 sum of the PKIX structure of the key.
func CreatePublicKeyFingerprint(key crypto.PublicKey) ([]byte, error) {
bytes, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return nil, err
}
checksum := sha256.Sum256(bytes)
return checksum[:], nil
}
+16 -2
View File
@@ -114,6 +114,11 @@ unknown = Unknown
rss_feed = RSS Feed
concept_system_global = Global
concept_user_individual = Individual
concept_code_repository = Repository
concept_user_organization = Organization
[aria]
navbar = Navigation Bar
footer = Footer
@@ -3217,6 +3222,15 @@ versions = Versions
versions.view_all = View all
dependency.id = ID
dependency.version = Version
alpine.registry = Setup this registry by adding the url in your <code>/etc/apk/repositories</code> file:
alpine.registry.key = Download the registry public RSA key into the <code>/etc/apk/keys/</code> folder to verify the index signature:
alpine.registry.info = Choose $branch and $repository from the list below.
alpine.install = To install the package, run the following command:
alpine.documentation = For more information on the Alpine registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
alpine.repository = Repository Info
alpine.repository.branches = Branches
alpine.repository.repositories = Repositories
alpine.repository.architectures = Architectures
cargo.registry = Setup this registry in the Cargo configuration file (for example <code>~/.cargo/config.toml</code>):
cargo.install = To install the package using Cargo, run the following command:
cargo.documentation = For more information on the Cargo registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
@@ -3259,6 +3273,8 @@ debian.repository.components = Components
debian.repository.architectures = Architectures
generic.download = Download package from the command line:
generic.documentation = For more information on the generic registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
go.install = Install the package from the command line:
go.documentation = For more information on the Go registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
helm.registry = Setup this registry from the command line:
helm.install = To install the package, run the following command:
helm.documentation = For more information on the Helm registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
@@ -3415,8 +3431,6 @@ runners.version = Version
runners.reset_registration_token_success = Runner registration token reset successfully
runs.all_workflows = All Workflows
runs.open_tab = %d Open
runs.closed_tab = %d Closed
runs.commit = Commit
runs.pushed_by = Pushed by
runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s
+96
View File
@@ -19,6 +19,7 @@ active_stopwatch=Suivi du temps actif
create_new=Créer…
user_profile_and_more=Profil et réglages…
signed_in_as=Connecté en tant que
enable_javascript=Ce site Web nécessite JavaScript.
toc=Sommaire
licenses=Licences
return_to_gitea=Revenir à Gitea
@@ -56,6 +57,7 @@ new_mirror=Nouveau miroir
new_fork=Nouvelle bifurcation
new_org=Nouvelle organisation
new_project=Nouveau projet
new_project_column=Nouvelle colonne
manage_org=Gérer les organisations
admin_panel=Administration du site
account_settings=Paramètres du compte
@@ -82,6 +84,7 @@ add=Ajouter
add_all=Tout Ajouter
remove=Retirer
remove_all=Tout Retirer
remove_label_str=Supprimer l’élément « %s »
edit=Éditer
enabled=Activé
@@ -89,9 +92,11 @@ disabled=Désactivé
copy=Copier
copy_url=Copier l'URL
copy_content=Copier le contenu
copy_branch=Copier le nom de la branche
copy_success=Copié !
copy_error=Échec de la copie
copy_type_unsupported=Ce type de fichier ne peut pas être copié
write=Écrire
preview=Aperçu
@@ -108,12 +113,36 @@ never=Jamais
rss_feed=Flux RSS
[aria]
navbar=Barre de navigation
footer=Pied de page
footer.software=À propos du logiciel
footer.links=Liens
[heatmap]
number_of_contributions_in_the_last_12_months=%s contributions au cours des 12 derniers mois
no_contributions=Aucune contribution
less=Moins
more=Plus
[editor]
buttons.heading.tooltip=Ajouter un en-tête
buttons.bold.tooltip=Ajouter du texte en gras
buttons.italic.tooltip=Ajouter du texte en italique
buttons.quote.tooltip=Citer le texte
buttons.code.tooltip=Ajouter du code
buttons.link.tooltip=Ajouter un lien
buttons.list.unordered.tooltip=Ajouter une liste à puces
buttons.list.ordered.tooltip=Ajouter une liste numérotée
buttons.list.task.tooltip=Ajouter une liste de tâches
buttons.mention.tooltip=Mentionner un utilisateur ou une équipe
buttons.ref.tooltip=Référencer un ticket ou demande dajout
buttons.switch_to_legacy.tooltip=Utiliser lancien éditeur à la place
buttons.enable_monospace_font=Activer la police à chasse fixe
buttons.disable_monospace_font=Désactiver la police à chasse fixe
[filter]
string.asc=A - Z
string.desc=Z - A
[error]
occurred=Une erreur sest produite
@@ -212,6 +241,7 @@ openid_signup_popup=Activer l'inscription avec OpenID.
enable_captcha=Activer le CAPTCHA d'inscription
enable_captcha_popup=Demander un CAPTCHA à l'inscription.
require_sign_in_view=Exiger la connexion à un compte pour afficher les pages
require_sign_in_view_popup=Limiter laccès aux pages aux utilisateurs connectés. Les visiteurs ne verront que les pages de connexion et dinscription.
admin_setting_desc=La création d'un compte administrateur est facultative. Le premier utilisateur enregistré deviendra automatiquement un administrateur le cas échéant.
admin_title=Paramètres de compte administrateur
admin_name=Nom dutilisateur administrateur
@@ -222,6 +252,7 @@ install_btn_confirm=Installer Gitea
test_git_failed=Le test de la commande "git" a échoué : %v
sqlite3_not_available=Cette version de Gitea ne supporte pas SQLite3. Veuillez télécharger la version binaire officielle de %s (pas la version 'gobuild').
invalid_db_setting=Les paramètres de la base de données sont invalides : %v
invalid_db_table=La table "%s" de la base de données est invalide : %v
invalid_repo_path=Le chemin racine du dépôt est invalide : %v
invalid_app_data_path=Le chemin des données de l'application est invalide : %v
run_user_not_match=Le nom d'utilisateur sous lequel Gitea est configuré n'est pas le nom d'utilisateur actuel: %s -> %s
@@ -239,6 +270,10 @@ default_enable_timetracking_popup=Activer le suivi du temps pour les nouveaux d
no_reply_address=Domaine pour les e-mails cachés
no_reply_address_helper=Nom de domaine pour les utilisateurs possédant une adresse email cachée. Par exemple, le nom dutilisateur « joe » sera enregistré dans Git comme « joe@noreply.example.org » si le domaine pour les e-mails cachés a la valeur « noreply.example.org ».
password_algorithm=Algorithme de hachage du mot de passe
invalid_password_algorithm=Algorithme de hachage du mot de passe invalide
password_algorithm_helper=Définissez lalgorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. Lalgorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources.
enable_update_checker=Activer la vérification des mises-à-jour
enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à gitea.io.
[home]
uname_holder=Nom d'utilisateur ou adresse e-mail
@@ -315,6 +350,7 @@ email_not_associate=L'adresse e-mail n'est associée à aucun compte.
send_reset_mail=Envoyer un e-mail de récupération du compte
reset_password=Récupération du compte
invalid_code=Votre code de confirmation est invalide ou a expiré.
invalid_password=Votre mot de passe ne correspond pas à celui utilisé pour créer le compte.
reset_password_helper=Récupérer un compte
reset_password_wrong_user=Vous êtes connecté en tant que %s, mais le lien de récupération du compte est pour %s
password_too_short=Le mot de passe doit contenir %d caractères minimum.
@@ -416,10 +452,15 @@ repo.transfer.body=Pour l'accepter ou le rejeter, visitez %s ou ignorez-le.
repo.collaborator.added.subject=%s vous a ajouté à %s
repo.collaborator.added.text=Vous avez été ajouté en tant que collaborateur du dépôt :
team_invite.subject=%[1]s vous a invité à rejoindre lorganisation %[2]s
team_invite.text_1=%[1]s vous a invité à rejoindre l’équipe %[2]s dans lorganisation %[3]s.
team_invite.text_2=Veuillez cliquer sur le lien suivant pour rejoindre l'équipe :
team_invite.text_3=Remarque : Cette invitation était destinée à %[1]s. Si vous nattendiez pas cette invitation, vous pouvez ignorer cet e-mail.
[modal]
yes=Oui
no=Non
confirm=Confirmer
cancel=Annuler
modify=Mettre à jour
@@ -454,8 +495,10 @@ size_error=` doit être à la taille de %s.`
min_size_error=` %s caractères minimum `
max_size_error=` %s caractères maximum `
email_error=` adresse e-mail invalide `
include_error=` doit contenir la sous-chaîne "%s".`
glob_pattern_error=` le motif de développement est invalide : %s.`
regex_pattern_error=` le motif regex est invalide : %s.`
username_error=` ne peut contenir que des caractères alphanumériques ('0-9','a-z','A-Z'), tiret ('-'), tiret de soulignement ('_') et point ('.'). Il ne peut pas commencer ou se finir par des caractères non alphanumériques. Les caractères non alphanumériques consécutifs sont également interdits.`
unknown_error=Erreur inconnue :
captcha_incorrect=Le code CAPTCHA est incorrect.
password_not_match=Les mots de passe ne correspondent pas.
@@ -476,6 +519,7 @@ team_name_been_taken=Le nom d'équipe est déjà pris.
team_no_units_error=Autoriser laccès à au moins une section du dépôt.
email_been_used=Cette adresse e-mail est déjà utilisée.
email_invalid=L'adresse e-mail est invalide.
openid_been_used=Adresse OpenID "%s" déjà utilisée.
username_password_incorrect=Identifiant ou mot de passe invalide.
password_complexity=Le mot de passe ne respecte pas les exigences de complexité:
password_lowercase_one=Au moins un caractère minuscule
@@ -490,12 +534,18 @@ user_not_exist=Cet utilisateur n'existe pas.
team_not_exist=L'équipe n'existe pas.
last_org_owner=Vous ne pouvez pas retirer le dernier utilisateur de l’équipe « propriétaires ». Il doit y avoir au moins un propriétaire dans chaque organisation.
cannot_add_org_to_team=Une organisation ne peut être ajoutée comme membre d'une équipe.
duplicate_invite_to_team=L'utilisateur a déjà été invité en tant que membre de l'équipe.
organization_leave_success=Vous avez quitté l'organisation %s avec succès.
invalid_ssh_key=Impossible de vérifier votre clé SSH : %s
invalid_gpg_key=Impossible de vérifier votre clé GPG : %s
invalid_ssh_principal=Principal invalide : %s
auth_failed=Échec d'authentification : %v
still_has_org=Votre compte est un membre dune ou plusieurs organisations, veuillez dabord les quitter.
still_own_packages=Votre compte possède toujours un ou plusieurs paquets, vous devez dabord les supprimer ou les transférer.
org_still_own_repo=Cette organisation possède encore un ou plusieurs dépôts. Vous devez dabord les supprimer ou les transférer.
org_still_own_packages=Cette organisation possède encore un ou plusieurs paquets. Vous devez dabord les supprimer.
target_branch_not_exist=La branche cible n'existe pas.
@@ -515,29 +565,73 @@ unfollow=Ne plus suivre
heatmap.loading=Chargement de la Heatmap…
user_bio=Biographie
disabled_public_activity=Cet utilisateur a désactivé la visibilité publique de l'activité.
email_visibility.limited=Votre adresse e-mail est visible pour tous les utilisateurs authentifiés
email_visibility.private=Votre adresse e-mail n'est visible que pour vous et les administrateurs
form.name_reserved=Le nom dutilisateur "%s" est réservé.
form.name_chars_not_allowed=Le nom d'utilisateur "%s" contient des caractères non valides.
[settings]
profile=Profil
account=Compte
appearance=Apparence
password=Mot de passe
security=Sécurité
avatar=Avatar
social=Réseaux Sociaux
applications=Applications
orgs=Gérer les organisations
repos=Dépôts
delete=Supprimer le compte
twofa=Authentification à deux facteurs
account_link=Comptes liés
organization=Organisations
public_profile=Profil public
biography_placeholder=Parlez-nous un peu de vous.
profile_desc=Votre adresse e-mail sera utilisée pour les notifications et d'autres opérations.
full_name=Non Complet
website=Site Web
location=Localisation
update_language_not_found=La langue "%s" n'est pas disponible.
update_language_success=La langue a été mise à jour.
update_profile_success=Votre profil a été mis à jour.
change_username=Votre nom d'utilisateur a été modifié.
change_username_prompt=Remarque : changer votre nom d'utilisateur change également l'URL de votre compte.
change_username_redirect_prompt=L'ancien nom d'utilisateur sera redirigé jusqu'à ce qu'il soit réclamé.
continue=Continuer
cancel=Annuler
language=Langue
ui=Thème
hidden_comment_types=Types de commentaires masqués
comment_type_group_reference=Référence
comment_type_group_milestone=Jalon
comment_type_group_assignee=Assigné à
comment_type_group_title=Titre
comment_type_group_branch=Branche
comment_type_group_dependency=Dépendance
comment_type_group_project=Projet
comment_type_group_issue_ref=Référence du ticket
saved_successfully=Vos paramètres ont été enregistrés avec succès.
privacy=Confidentialité
keep_activity_private=Masquer l'activité de la page de profil
keep_activity_private_popup=Rend l'activité visible uniquement pour vous et les administrateurs
lookup_avatar_by_mail=Rechercher un avatar par adresse e-mail
enable_custom_avatar=Utiliser un avatar personnalisé
choose_new_avatar=Sélectionner un nouvel avatar
delete_current_avatar=Supprimer l'avatar actuel
uploaded_avatar_not_a_image=Le fichier téléchargé n'est pas une image.
update_avatar_success=Votre avatar a été mis à jour.
update_user_avatar_success=L'avatar de l'utilisateur a été mis à jour.
password_change_disabled=Les mots de passes des comptes utilisateurs externes ne peuvent pas être modifiées depuis l'interface web Gitea.
primary=Principale
activated=Activé
delete_email=Exclure
openid_deletion_success=L'adresse OpenID a été supprimée.
add_new_openid=Ajouter une nouvelle URI OpenID
gpg_key_verify=Vérifier
gpg_token=Jeton
@@ -909,6 +1003,7 @@ projects.template.desc_helper=Sélectionnez un modèle de projet pour débuter
projects.type.uncategorized=Non catégorisé
projects.column.edit_title=Nom
projects.column.new_title=Nom
projects.column.new=Nouvelle colonne
projects.column.color=Couleur
projects.open=Ouvrir
projects.close=Fermer
@@ -982,6 +1077,7 @@ issues.filter_label_exclude=`Utiliser <code>alt</code> + <code>clic/entrée</cod
issues.filter_label_no_select=Toutes les étiquettes
issues.filter_milestone=Jalon
issues.filter_milestone_no_select=Tous les jalons
issues.filter_project=Projet
issues.filter_project_none=Pas de projet
issues.filter_assignee=Assigné
issues.filter_assginee_no_select=Toutes les affectations
+44
View File
@@ -19,6 +19,7 @@ active_stopwatch=Cronômetro ativo
create_new=Criar…
user_profile_and_more=Perfil e Configurações...
signed_in_as=Acessado como
enable_javascript=Este site requer JavaScript.
toc=Índice
licenses=Licenças
return_to_gitea=Volte para Gitea
@@ -83,6 +84,7 @@ add=Adicionar
add_all=Adicionar todos
remove=Remover
remove_all=Excluir todos
remove_label_str=`Remover item "%s"`
edit=Editar
enabled=Habilitado
@@ -117,8 +119,25 @@ footer.software=Sobre o Software
footer.links=Links
[heatmap]
number_of_contributions_in_the_last_12_months=%s contribuições nos últimos 12 meses
no_contributions=Sem contribuições
less=Menos
more=Mais
[editor]
buttons.heading.tooltip=Adicionar título
buttons.bold.tooltip=Adicionar texto em negrito
buttons.italic.tooltip=Adicionar texto em itálico
buttons.quote.tooltip=Citar texto
buttons.code.tooltip=Adicionar código
buttons.link.tooltip=Adicionar um link
buttons.list.unordered.tooltip=Adicionar uma lista com marcadores
buttons.list.ordered.tooltip=Adicionar uma lista numerada
buttons.list.task.tooltip=Adicionar uma lista de tarefas
buttons.mention.tooltip=Mencionar um usuário ou equipe
buttons.switch_to_legacy.tooltip=Em vez disso, usar o editor legado
buttons.enable_monospace_font=Habilitar fonte mono espaçada
buttons.disable_monospace_font=Desabilitar fonte mono espaçada
[filter]
string.asc=A - Z
@@ -231,6 +250,7 @@ install_btn_confirm=Instalar Gitea
test_git_failed=Falha ao testar o comando 'git': %v
sqlite3_not_available=Esta versão do Gitea não suporta SQLite3. Por favor faça o download da versão binária oficial em %s (não utilize a versão 'gobuild').
invalid_db_setting=Configuração de banco de dados está inválida: %v
invalid_db_table=A tabela "%s" do banco de dados é inválida: %v
invalid_repo_path=A raiz do repositório está inválida: %v
invalid_app_data_path=O caminho dos dados do aplicativo é inválido: %v
run_user_not_match=O nome de usuário 'Executar como' não é o nome de usuário atual: %s -> %s
@@ -249,6 +269,7 @@ no_reply_address=Domínio de e-mail oculto
no_reply_address_helper=Nome de domínio para usuários com um endereço de e-mail oculto. Por exemplo, o nome de usuário 'joe' será registrado no Git como 'joe@noreply.example.org' se o domínio de e-mail oculto estiver definido como 'noreply.example.org'.
password_algorithm=Algoritmo Hash de Senha
invalid_password_algorithm=Algoritmo de hash de senha inválido
password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O algoritmo argon2 é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas com menos recursos.
enable_update_checker=Habilitar Verificador de Atualizações
enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io.
@@ -295,6 +316,7 @@ repo_no_results=Nenhum repositório correspondente foi encontrado.
user_no_results=Nenhum usuário correspondente foi encontrado.
org_no_results=Nenhuma organização correspondente foi encontrada.
code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
code_search_results=`Resultados da pesquisa por: "%s"`
code_last_indexed_at=Última indexação %s
relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos.
relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, <a href="%s">mostrar resultados não filtrados</a>.
@@ -472,6 +494,8 @@ size_error=`deve ser do tamanho %s.`
min_size_error=` deve conter pelo menos %s caracteres.`
max_size_error=` deve conter no máximo %s caracteres.`
email_error=` não é um endereço de e-mail válido.`
url_error= `"%s" não é um URL válido.`
include_error=` deve conter "%s".`
glob_pattern_error=` padrão glob é inválido: %s.`
regex_pattern_error=` o regex é inválido: %s.`
username_error=` só pode conter caracteres alfanuméricos ('0-9','a-z','A-Z'), traço ('-'), sublinhado ('_') e ponto ('.'). Não pode começar ou terminar com caracteres não alfanuméricos, e caracteres não-alfanuméricos consecutivos também são proibidos.`
@@ -496,6 +520,7 @@ team_name_been_taken=O nome da equipe já está sendo usado.
team_no_units_error=Permitir acesso a pelo menos uma seção de repositório.
email_been_used=Este endereço de e-mail já está sendo usado.
email_invalid=O endereço de email é inválido.
openid_been_used=O endereço OpenID "%s" já está sendo usado.
username_password_incorrect=Nome de usuário ou senha incorretos.
password_complexity=A senha não passa pelos requisitos de complexidade:
password_lowercase_one=Pelo menos um caractere minúsculo
@@ -517,8 +542,12 @@ invalid_ssh_key=Não é possível verificar sua chave SSH: %s
invalid_gpg_key=Não é possível verificar sua chave GPG: %s
invalid_ssh_principal=Nome principal inválido: %s
must_use_public_key=A chave que você forneceu é uma chave privada. Por favor, não envie sua chave privada em nenhum lugar. Use sua chave pública em vez disso.
unable_verify_ssh_key=Não é possível verificar a chave SSH, verifique se há erros.
auth_failed=Autenticação falhou: %v
still_own_repo=Sua conta possui um ou mais repositórios, exclua ou transfira-os primeiro.
still_has_org=Sua conta é um membro de uma ou mais organizações, deixe-as primeiro.
still_own_packages=Sua conta possui um ou mais pacotes, exclua-os primeiro.
target_branch_not_exist=O branch de destino não existe.
@@ -538,7 +567,12 @@ unfollow=Deixar de seguir
heatmap.loading=Carregando mapa de calor...
user_bio=Biografia
disabled_public_activity=Este usuário desativou a visibilidade pública da atividade.
email_visibility.limited=Seu endereço de e-mail está visível para todos os usuários autenticados
email_visibility.private=Seu endereço de e-mail é visível apenas para você e administradores
form.name_reserved=O nome de usuário "%s" está reservado.
form.name_pattern_not_allowed=O padrão de "%s" não é permitido em um nome de usuário.
form.name_chars_not_allowed=Nome de usuário "%s" contém caracteres inválidos.
[settings]
profile=Perfil
@@ -569,6 +603,7 @@ location=Localização
update_theme=Atualizar o tema
update_profile=Atualizar o perfil
update_language=Atualizar Idioma
update_language_not_found=Idioma "%s" não está disponível.
update_language_success=O idioma foi atualizado.
update_profile_success=Seu perfil foi atualizado.
change_username=Seu nome de usuário foi alterado.
@@ -821,6 +856,7 @@ email_notifications.andyourown=E Suas Próprias Notificações
visibility=Visibilidade do usuário
visibility.public=Pública
visibility.public_tooltip=Visível para todos
visibility.limited=Limitada
visibility.private=Privada
visibility.private_tooltip=Visível apenas para membros da organização
@@ -2203,6 +2239,8 @@ diff.image.side_by_side=Lado a Lado
diff.image.swipe=Deslizar
diff.image.overlay=Sobrepor
diff.has_escaped=Essa linha tem caracteres Unicode ocultos
diff.show_file_tree=Mostrar árvore de arquivos
diff.hide_file_tree=Ocultar árvore de arquivos
releases.desc=Acompanhe as versões e downloads do projeto.
release.releases=Versões
@@ -2216,6 +2254,7 @@ release.compare=Comparar
release.edit=editar
release.ahead.commits=<strong>%d</strong> commits
release.ahead.target=para %s desde esta versão
tag.ahead.target=para %s desde esta tag
release.source_code=Código fonte
release.new_subheader=Lançamentos organizam versões do projeto.
release.edit_subheader=Lançamentos organizam versões do projeto.
@@ -2224,6 +2263,9 @@ release.target=Destino
release.tag_helper=Escolha uma tag existente, ou crie uma nova tag.
release.tag_helper_new=Nova tag. Esta tag será criada a partir do alvo.
release.tag_helper_existing=Tag existente.
release.title=Título da versão
release.title_empty=O título não pode estar em branco.
release.message=Descreva esta versão
release.prerelease_desc=Marcar como pré-lançamento
release.prerelease_helper=Marcar esta versão como inadequada para uso em produção.
release.cancel=Cancelar
@@ -2250,7 +2292,9 @@ release.tags_for=Tags para %s
branch.name=Nome do Branch
branch.search=Pesquisar branches
branch.already_exists=Um branch com o nome "%s" já existe.
branch.delete_head=Excluir
branch.delete=`Excluir branch "%s"`
branch.delete_html=Excluir Branch
branch.delete_desc=A exclusão de um branch é permanente. Isto <strong>NÃO PODERÁ</strong> ser desfeito. Continuar?
branch.create_branch=Criar branch <strong>%s</strong>
+319 -245
View File
@@ -7,17 +7,17 @@
"name": "gitea",
"license": "MIT",
"dependencies": {
"@citation-js/core": "0.6.5",
"@citation-js/plugin-bibtex": "0.6.6",
"@citation-js/plugin-csl": "0.6.7",
"@citation-js/core": "0.6.8",
"@citation-js/plugin-bibtex": "0.6.8",
"@citation-js/plugin-csl": "0.6.8",
"@citation-js/plugin-software-formats": "0.6.1",
"@claviska/jquery-minicolors": "2.3.6",
"@github/markdown-toolbar-element": "2.1.1",
"@github/relative-time-element": "4.3.0",
"@github/text-expander-element": "2.3.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "18.3.0",
"@vue/compiler-sfc": "3.2.47",
"@primer/octicons": "19.1.0",
"@vue/compiler-sfc": "3.3.2",
"@webcomponents/custom-elements": "1.6.0",
"add-asset-webpack-plugin": "2.0.1",
"ansi-to-html": "0.7.2",
@@ -29,7 +29,7 @@
"esbuild-loader": "3.0.1",
"escape-goat": "4.0.0",
"fast-glob": "3.2.12",
"jquery": "3.6.4",
"jquery": "3.7.0",
"jquery.are-you-sure": "1.9.0",
"katex": "0.16.7",
"license-checker-webpack-plugin": "0.2.1",
@@ -44,30 +44,32 @@
"tippy.js": "6.3.7",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
"vue": "3.2.47",
"vue": "3.3.2",
"vue-bar-graph": "2.0.0",
"vue-loader": "17.1.0",
"vue-loader": "17.1.1",
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.82.0",
"webpack-cli": "5.1.0",
"webpack": "5.82.1",
"webpack-cli": "5.1.1",
"workbox-routing": "6.5.4",
"workbox-strategies": "6.5.4",
"wrap-ansi": "8.1.0"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "3.2.1",
"@playwright/test": "1.33.0",
"@rollup/pluginutils": "5.0.2",
"@stoplight/spectral-cli": "6.6.0",
"@vitejs/plugin-vue": "4.2.1",
"@vitejs/plugin-vue": "4.2.3",
"eslint": "8.40.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jquery": "1.5.1",
"eslint-plugin-no-jquery": "2.7.0",
"eslint-plugin-regexp": "1.14.0",
"eslint-plugin-regexp": "1.15.0",
"eslint-plugin-sonarjs": "0.19.0",
"eslint-plugin-unicorn": "47.0.0",
"eslint-plugin-vue": "9.11.1",
"eslint-plugin-vue": "9.12.0",
"eslint-plugin-wc": "1.5.0",
"jsdom": "22.0.0",
"markdownlint-cli": "0.34.0",
"stylelint": "15.6.1",
@@ -81,9 +83,9 @@
}
},
"node_modules/@asyncapi/specs": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.2.1.tgz",
"integrity": "sha512-NeOgMxl1J00PbsmStbDFE6OVA6rE5S3xv0Twm70MwMigsE28hdG9iH/+2SrMBeD/NFKVlryHcEuLtAPoqiCCFg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.3.1.tgz",
"integrity": "sha512-EfexhJu/lwF8OdQDm28NKLJHFkx0Gb6O+rcezhZYLPIoNYKXJMh2J1vFGpwmfAcTTh+ffK44Oc2Hs1Q4sLBp+A==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.11"
@@ -223,9 +225,9 @@
"integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg=="
},
"node_modules/@citation-js/core": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.5.tgz",
"integrity": "sha512-YmfL3wby/oLgggs3hqRcllL0xYOUzTaABChTEEbcfXwrvIstgHzODG1tcPAVg/EVuVH151uMR9xttuzu+Lbxcg==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.8.tgz",
"integrity": "sha512-EqnEj+0OR9t0pU0d/CQ4TKNSxxALTSm5gXxg56dQ2nnod9esIwNVDWztX5LXstiHj7k8VjUSo/TZw7O2AXFdBQ==",
"dependencies": {
"@citation-js/date": "^0.5.0",
"@citation-js/name": "^0.4.2",
@@ -253,9 +255,9 @@
}
},
"node_modules/@citation-js/plugin-bibtex": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.6.tgz",
"integrity": "sha512-hDcMK+e+WaA8f3b+SkIVB+41w39Yf3AVGQ6Ee1amC4KCF5kS6IoiAC5dUquCZoaJCrh5PEg1fyX2tuii6WkOhA==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.8.tgz",
"integrity": "sha512-ERCHi4TTZ/8TM/Y6lul/pcLHfMltcZqn0BrATCXwVrndwXdUfj8Z6bQdXgaOAhHjo7ngp9T0DC7skgofEGBSTA==",
"dependencies": {
"@citation-js/date": "^0.5.0",
"@citation-js/name": "^0.4.2",
@@ -281,9 +283,9 @@
}
},
"node_modules/@citation-js/plugin-csl": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.7.tgz",
"integrity": "sha512-cgMCaujDaSnYPHmmyk5vp7UTVmEEkToqh1gFH5OGUOlLOaFmRTQn8kssMrVQR/mBZlQmMeoh5NYVktNeo5eDCg==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.8.tgz",
"integrity": "sha512-X3cTCKWvhD580YSVcS55XhwVgPppzDIiaYgiJopAa/MAvzogt1ODqy6dbBPtb07ttdVQm8VG8FHRD2LsGViTGg==",
"dependencies": {
"@citation-js/date": "^0.5.0",
"citeproc": "^2.4.6"
@@ -765,6 +767,31 @@
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-plugin-eslint-comments": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.1.tgz",
"integrity": "sha512-/HZbjIGaVO2zLlWX3gRgiHmKRVvvqrC0zVu3eXnIj1ORxoyfGSj50l0PfDfqihyZAqrDYzSMdJesXzFjvAoiLQ==",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.5",
"ignore": "^5.2.4"
},
"engines": {
"node": ">=6.5.0"
},
"peerDependencies": {
"eslint": ">=4.19.1"
}
},
"node_modules/@eslint-community/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -1174,9 +1201,9 @@
}
},
"node_modules/@primer/octicons": {
"version": "18.3.0",
"resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-18.3.0.tgz",
"integrity": "sha512-vyUej5rKjXfqHAmYSo2k3pA9IsL5KJaYOBVXvc188FwBfUweA+WVP3qwWyZTSQnp9LK6yOajGyr0SCMIa9ombw==",
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.1.0.tgz",
"integrity": "sha512-5o90F89gNPnAk1qfzl3hb/TcsUjk5g0WFI+fBRHLkBKzB3uc9EvxTpgzjXhhjyriOkrBOjFo58D0sjtwttaQww==",
"dependencies": {
"object-assign": "^4.1.1"
}
@@ -1630,9 +1657,9 @@
}
},
"node_modules/@stoplight/types": {
"version": "13.14.0",
"resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.14.0.tgz",
"integrity": "sha512-RqF5Cyl5227fRPIaWa5ptMvAtNUIM4yCzSUoERUXAarxpTcOrjMJ52p9lV9XrQtpLhLNMrWn08+h0ap10R22ig==",
"version": "13.15.0",
"resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.15.0.tgz",
"integrity": "sha512-pBLjVRrWGVd+KzTbL3qrmufSKIEp0UfziDBdt/nrTHPKrlrtVwaHdrrQMcpM23yJDU1Wcg4cHvhIuGtKCT5OmA==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.4",
@@ -1764,9 +1791,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.1.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.1.tgz",
"integrity": "sha512-uKBEevTNb+l6/aCQaKVnUModfEMjAl98lw2Si9P5y4hLu9tm6AlX2ZIoXZX6Wh9lJueYPrGPKk5WMCNHg/u6/A=="
"version": "20.1.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.3.tgz",
"integrity": "sha512-NP2yfZpgmf2eDRPmgGq+fjGjSwFgYbihA8/gK+ey23qT9RkxsgNTZvGOEpXgzIGqesTYkElELLgtKoMQTys5vA=="
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
@@ -1809,9 +1836,9 @@
"dev": true
},
"node_modules/@vitejs/plugin-vue": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.1.tgz",
"integrity": "sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
"integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
"dev": true,
"engines": {
"node": "^14.18.0 || >=16.0.0"
@@ -1930,240 +1957,257 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.2.tgz",
"integrity": "sha512-CKZWo1dzsQYTNTft7whzjL0HsrEpMfiK7pjZ2WFE3bC1NA7caUjWioHSK+49y/LK7Bsm4poJZzAMnvZMQ7OTeg==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.47",
"@babel/parser": "^7.21.3",
"@vue/shared": "3.3.2",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.2.tgz",
"integrity": "sha512-6gS3auANuKXLw0XH6QxkWqyPYPunziS2xb6VRenM3JY7gVfZcJvkCBHkb5RuNY1FCbBO3lkIi0CdXUCW1c7SXw==",
"dependencies": {
"@vue/compiler-core": "3.2.47",
"@vue/shared": "3.2.47"
"@vue/compiler-core": "3.3.2",
"@vue/shared": "3.3.2"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.2.tgz",
"integrity": "sha512-jG4jQy28H4BqzEKsQqqW65BZgmo3vzdLHTBjF+35RwtDdlFE+Fk1VWJYUnDMMqkFBo6Ye1ltSKVOMPgkzYj7SQ==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.47",
"@vue/compiler-dom": "3.2.47",
"@vue/compiler-ssr": "3.2.47",
"@vue/reactivity-transform": "3.2.47",
"@vue/shared": "3.2.47",
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.2",
"@vue/compiler-dom": "3.3.2",
"@vue/compiler-ssr": "3.3.2",
"@vue/reactivity-transform": "3.3.2",
"@vue/shared": "3.3.2",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"magic-string": "^0.30.0",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-sfc/node_modules/magic-string": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.2.tgz",
"integrity": "sha512-K8OfY5FQtZaSOJHHe8xhEfIfLrefL/Y9frv4k4NsyQL3+0lRKxr9QuJhfdBDjkl7Fhz8CzKh63mULvmOfx3l2w==",
"dependencies": {
"@vue/compiler-dom": "3.2.47",
"@vue/shared": "3.2.47"
"@vue/compiler-dom": "3.3.2",
"@vue/shared": "3.3.2"
}
},
"node_modules/@vue/reactivity": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.2.tgz",
"integrity": "sha512-yX8C4uTgg2Tdj+512EEMnMKbLveoITl7YdQX35AYgx8vBvQGszKiiCN46g4RY6/deeo/5DLbeUUGxCq1qWMf5g==",
"dependencies": {
"@vue/shared": "3.2.47"
"@vue/shared": "3.3.2"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.2.tgz",
"integrity": "sha512-iu2WaQvlJHdnONrsyv4ibIEnSsuKF+aHFngGj/y1lwpHQtalpVhKg9wsKMoiKXS9zPNjG9mNKzJS9vudvjzvyg==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.47",
"@vue/shared": "3.2.47",
"@babel/parser": "^7.20.15",
"@vue/compiler-core": "3.3.2",
"@vue/shared": "3.3.2",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
"magic-string": "^0.30.0"
}
},
"node_modules/@vue/reactivity-transform/node_modules/magic-string": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.2.tgz",
"integrity": "sha512-qSl95qj0BvKfcsO+hICqFEoLhJn6++HtsPxmTkkadFbuhe3uQfJ8HmQwvEr7xbxBd2rcJB6XOJg7nWAn/ymC5A==",
"dependencies": {
"@vue/reactivity": "3.2.47",
"@vue/shared": "3.2.47"
"@vue/reactivity": "3.3.2",
"@vue/shared": "3.3.2"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.2.tgz",
"integrity": "sha512-+drStsJT+0mtgHdarT7cXZReCcTFfm6ptxMrz0kAW5hms6UNBd8Q1pi4JKlncAhu+Ld/TevsSp7pqAZxBBoGng==",
"dependencies": {
"@vue/runtime-core": "3.2.47",
"@vue/shared": "3.2.47",
"csstype": "^2.6.8"
"@vue/runtime-core": "3.3.2",
"@vue/shared": "3.3.2",
"csstype": "^3.1.1"
}
},
"node_modules/@vue/runtime-dom/node_modules/csstype": {
"version": "2.6.21",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/@vue/server-renderer": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.2.tgz",
"integrity": "sha512-QCwh6OGwJg6GDLE0fbQhRTR6tnU+XDJ1iCsTYHXBiezCXAhqMygFRij7BiLF4ytvvHcg5kX9joX5R5vP85++wg==",
"dependencies": {
"@vue/compiler-ssr": "3.2.47",
"@vue/shared": "3.2.47"
"@vue/compiler-ssr": "3.3.2",
"@vue/shared": "3.3.2"
},
"peerDependencies": {
"vue": "3.2.47"
"vue": "3.3.2"
}
},
"node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.2.tgz",
"integrity": "sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz",
"integrity": "sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.5",
"@webassemblyjs/helper-wasm-bytecode": "1.11.5"
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
}
},
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz",
"integrity": "sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ=="
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz",
"integrity": "sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA=="
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz",
"integrity": "sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg=="
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz",
"integrity": "sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.5",
"@webassemblyjs/helper-api-error": "1.11.5",
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.5.tgz",
"integrity": "sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA=="
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.5.tgz",
"integrity": "sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/helper-buffer": "1.11.5",
"@webassemblyjs/helper-wasm-bytecode": "1.11.5",
"@webassemblyjs/wasm-gen": "1.11.5"
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
}
},
"node_modules/@webassemblyjs/ieee754": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.5.tgz",
"integrity": "sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
},
"node_modules/@webassemblyjs/leb128": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.5.tgz",
"integrity": "sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
"dependencies": {
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.5.tgz",
"integrity": "sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ=="
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.5.tgz",
"integrity": "sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/helper-buffer": "1.11.5",
"@webassemblyjs/helper-wasm-bytecode": "1.11.5",
"@webassemblyjs/helper-wasm-section": "1.11.5",
"@webassemblyjs/wasm-gen": "1.11.5",
"@webassemblyjs/wasm-opt": "1.11.5",
"@webassemblyjs/wasm-parser": "1.11.5",
"@webassemblyjs/wast-printer": "1.11.5"
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.5.tgz",
"integrity": "sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/helper-wasm-bytecode": "1.11.5",
"@webassemblyjs/ieee754": "1.11.5",
"@webassemblyjs/leb128": "1.11.5",
"@webassemblyjs/utf8": "1.11.5"
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
}
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.5.tgz",
"integrity": "sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/helper-buffer": "1.11.5",
"@webassemblyjs/wasm-gen": "1.11.5",
"@webassemblyjs/wasm-parser": "1.11.5"
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.5.tgz",
"integrity": "sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/helper-api-error": "1.11.5",
"@webassemblyjs/helper-wasm-bytecode": "1.11.5",
"@webassemblyjs/ieee754": "1.11.5",
"@webassemblyjs/leb128": "1.11.5",
"@webassemblyjs/utf8": "1.11.5"
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
}
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.5.tgz",
"integrity": "sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==",
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"dependencies": {
"@webassemblyjs/ast": "1.11.5",
"@webassemblyjs/ast": "1.11.6",
"@xtuc/long": "4.2.2"
}
},
@@ -2197,9 +2241,9 @@
}
},
"node_modules/@webpack-cli/serve": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.3.tgz",
"integrity": "sha512-Bwxd73pHuYc0cyl7vulPp2I6kAYtmJPkfUivbts7by6wDAVyFdKzGX3AksbvCRyNVFUJu7o2ZTcWXdT90T3qbg==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.4.tgz",
"integrity": "sha512-0xRgjgDLdz6G7+vvDLlaRpFatJaJ69uTalZLRSMX5B3VUrDmXcrVA3+6fXXQgmYz7bY9AAgs348XQdmtLsK41A==",
"engines": {
"node": ">=14.15.0"
},
@@ -2253,9 +2297,9 @@
}
},
"node_modules/acorn-import-assertions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"peerDependencies": {
"acorn": "^8"
}
@@ -4032,9 +4076,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.386",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.386.tgz",
"integrity": "sha512-w0VD4WR225nuNsz6FokDaqugxzue6iUVBo8QfUrl2Y6nWHxtBUhjWDnUaG/1v5oWeFPLMJAQk3zKHTHW/P8+Og=="
"version": "1.4.392",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.392.tgz",
"integrity": "sha512-TXQOMW9tnhIms3jGy/lJctLjICOgyueZFJ1KUtm6DTQ+QpxX3p7ZBwB6syuZ9KBuT5S4XX7bgY1ECPgfxKUdOg=="
},
"node_modules/elkjs": {
"version": "0.8.2",
@@ -4064,9 +4108,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz",
"integrity": "sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==",
"version": "5.14.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz",
"integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -4579,9 +4623,9 @@
}
},
"node_modules/eslint-plugin-regexp": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-1.14.0.tgz",
"integrity": "sha512-5+bBSsRTTtkSf8+/iNSjiOW6qbjAdGyqv88HxPaBNFKxROK+UAdOGDl5Jr+csV5wW2BuOOvaG82zsvTriQBRFA==",
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-1.15.0.tgz",
"integrity": "sha512-YEtQPfdudafU7RBIFci81R/Q1yErm0mVh3BkGnXD2Dk8DLwTFdc2ITYH1wCnHKim2gnHfPFgrkh+b2ozyyU7ag==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
@@ -4646,9 +4690,9 @@
}
},
"node_modules/eslint-plugin-vue": {
"version": "9.11.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.11.1.tgz",
"integrity": "sha512-SNtBGDrRkPUFsREswPceqdvZ7YVdWY+iCYiDC+RoxwVieeQ7GJU1FLDlkcaYTOD2os/YuVgI1Fdu8YGM7fmoow==",
"version": "9.12.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.12.0.tgz",
"integrity": "sha512-xH8PgpDW2WwmFSmRfs/3iWogef1CJzQqX264I65zz77jDuxF2yLy7+GA2diUM8ZNATuSl1+UehMQkb5YEyau5w==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.3.0",
@@ -4666,6 +4710,19 @@
"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/eslint-plugin-wc": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.5.0.tgz",
"integrity": "sha512-KFSfiHDol/LeV7U6IX8GdgpGf/s3wG8FTG120Rml/hGNB/DkCuGYQhlf0VgdBdf7gweem8Nlsh5o64HNdj+qPA==",
"dev": true,
"dependencies": {
"is-valid-element-name": "^1.0.0",
"js-levenshtein-esm": "^1.2.0"
},
"peerDependencies": {
"eslint": ">=5"
}
},
"node_modules/eslint-scope": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
@@ -5977,6 +6034,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-valid-element-name": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz",
"integrity": "sha512-GZITEJY2LkSjQfaIPBha7eyZv+ge0PhBR7KITeCCWvy7VBQrCUdFkvpI+HrAPQjVtVjy1LvlEkqQTHckoszruw==",
"dev": true,
"dependencies": {
"is-potential-custom-element-name": "^1.0.0"
}
},
"node_modules/is-weakref": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@@ -6054,9 +6120,9 @@
}
},
"node_modules/jquery": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
"integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz",
"integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ=="
},
"node_modules/jquery.are-you-sure": {
"version": "1.9.0",
@@ -6069,6 +6135,12 @@
"node": ">=0.8.0"
}
},
"node_modules/js-levenshtein-esm": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
"integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
"dev": true
},
"node_modules/js-sdsl": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
@@ -6492,6 +6564,7 @@
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"dependencies": {
"sourcemap-codec": "^1.4.8"
}
@@ -6591,9 +6664,9 @@
}
},
"node_modules/markdownlint-cli/node_modules/glob": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.2.2.tgz",
"integrity": "sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==",
"version": "10.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.2.3.tgz",
"integrity": "sha512-Kb4rfmBVE3eQTAimgmeqc2LwSnN0wIOkkUL6HmxEFxNJ4fHghYHVbFba/HcGcRjE6s9KoMNK3rSOwkL4PioZjg==",
"dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
@@ -6909,15 +6982,15 @@
}
},
"node_modules/mlly": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.0.tgz",
"integrity": "sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.1.tgz",
"integrity": "sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==",
"dev": true,
"dependencies": {
"acorn": "^8.8.2",
"pathe": "^1.1.0",
"pkg-types": "^1.0.2",
"ufo": "^1.1.1"
"pkg-types": "^1.0.3",
"ufo": "^1.1.2"
}
},
"node_modules/monaco-editor": {
@@ -7015,9 +7088,9 @@
}
},
"node_modules/node-fetch": {
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.10.tgz",
"integrity": "sha512-5YytjUVbwzjE/BX4N62vnPPkGNxlJPwdA9/ArUc4pcM6cYS4Hinuv4VazzwjMGgnWuiQqcemOanib/5PpcsGug==",
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -7386,12 +7459,12 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-scurry": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz",
"integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.8.0.tgz",
"integrity": "sha512-IjTrKseM404/UAWA8bBbL3Qp6O2wXkanuIE3seCxBH7ctRuvH1QRawy1N3nVDHGkdeZsjOsSe/8AQBL/VQCy2g==",
"dev": true,
"dependencies": {
"lru-cache": "^9.0.0",
"lru-cache": "^9.1.1",
"minipass": "^5.0.0"
},
"engines": {
@@ -8520,9 +8593,9 @@
"dev": true
},
"node_modules/signal-exit": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz",
"integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
"integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==",
"dev": true,
"engines": {
"node": ">=14"
@@ -8661,7 +8734,8 @@
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
"dev": true
},
"node_modules/spdx-compare": {
"version": "1.0.0",
@@ -9130,9 +9204,9 @@
}
},
"node_modules/terser": {
"version": "5.17.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.2.tgz",
"integrity": "sha512-1D1aGbOF1Mnayq5PvfMc0amAR1y5Z1nrZaGCvI5xsdEfZEVte8okonk02OiaK5fw5hG1GWuuVsakOnpZW8y25A==",
"version": "5.17.3",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz",
"integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==",
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -9678,9 +9752,9 @@
}
},
"node_modules/vite/node_modules/rollup": {
"version": "3.21.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz",
"integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==",
"version": "3.21.6",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.6.tgz",
"integrity": "sha512-SXIICxvxQxR3D4dp/3LDHZIJPC8a4anKMHd4E3Jiz2/JnY+2bEjqrOokAauc5ShGVNFHlEFjBXAXlaxkJqIqSg==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
@@ -9784,9 +9858,9 @@
}
},
"node_modules/vm2": {
"version": "3.9.17",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz",
"integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==",
"version": "3.9.18",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.18.tgz",
"integrity": "sha512-iM7PchOElv6Uv6Q+0Hq7dcgDtWWT6SizYqVcvol+1WQc+E9HlgTCnPozbQNSP3yDV9oXHQOEQu530w2q/BCVZg==",
"dev": true,
"dependencies": {
"acorn": "^8.7.0",
@@ -9800,15 +9874,15 @@
}
},
"node_modules/vue": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.2.tgz",
"integrity": "sha512-98hJcAhyDwZoOo2flAQBSPVYG/o0HA9ivIy2ktHshjE+6/q8IMQ+kvDKQzOZTFPxvnNMcGM+zS2A00xeZMA7tA==",
"dependencies": {
"@vue/compiler-dom": "3.2.47",
"@vue/compiler-sfc": "3.2.47",
"@vue/runtime-dom": "3.2.47",
"@vue/server-renderer": "3.2.47",
"@vue/shared": "3.2.47"
"@vue/compiler-dom": "3.3.2",
"@vue/compiler-sfc": "3.3.2",
"@vue/runtime-dom": "3.3.2",
"@vue/server-renderer": "3.3.2",
"@vue/shared": "3.3.2"
}
},
"node_modules/vue-bar-graph": {
@@ -9845,9 +9919,9 @@
}
},
"node_modules/vue-loader": {
"version": "17.1.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.1.0.tgz",
"integrity": "sha512-zAjrT+TNWTpgRODxqDfzbDyvuTf5kCP9xmMk8aspQKuYNnTY2r0XK/bHu1DKLpSpk0I6fkQph5OLKB7HcRIPZw==",
"version": "17.1.1",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.1.1.tgz",
"integrity": "sha512-qpqEVkKdrAsgyIBMHaiXurDeCuBWqRyKqg2GI4aG3NbggEls+BLqTZdqahbJJh7fm83sz+iz3gg6eDWdbNlG7Q==",
"dependencies": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
@@ -9916,9 +9990,9 @@
}
},
"node_modules/webpack": {
"version": "5.82.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.0.tgz",
"integrity": "sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg==",
"version": "5.82.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.1.tgz",
"integrity": "sha512-C6uiGQJ+Gt4RyHXXYt+v9f+SN1v83x68URwgxNQ98cvH8kxiuywWGP4XeNZ1paOzZ63aY3cTciCEQJNFUljlLw==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
@@ -9929,7 +10003,7 @@
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.13.0",
"enhanced-resolve": "^5.14.0",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -9962,14 +10036,14 @@
}
},
"node_modules/webpack-cli": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.0.tgz",
"integrity": "sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.1.tgz",
"integrity": "sha512-OLJwVMoXnXYH2ncNGU8gxVpUtm3ybvdioiTvHgUyBuyMLKiVvWy+QObzBsMtp5pH7qQoEuWgeEUQ/sU3ZJFzAw==",
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^2.1.0",
"@webpack-cli/info": "^2.0.1",
"@webpack-cli/serve": "^2.0.3",
"@webpack-cli/serve": "^2.0.4",
"colorette": "^2.0.14",
"commander": "^10.0.1",
"cross-spawn": "^7.0.3",
+15 -13
View File
@@ -7,17 +7,17 @@
"node": ">= 16.0.0"
},
"dependencies": {
"@citation-js/core": "0.6.5",
"@citation-js/plugin-bibtex": "0.6.6",
"@citation-js/plugin-csl": "0.6.7",
"@citation-js/core": "0.6.8",
"@citation-js/plugin-bibtex": "0.6.8",
"@citation-js/plugin-csl": "0.6.8",
"@citation-js/plugin-software-formats": "0.6.1",
"@claviska/jquery-minicolors": "2.3.6",
"@github/markdown-toolbar-element": "2.1.1",
"@github/relative-time-element": "4.3.0",
"@github/text-expander-element": "2.3.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "18.3.0",
"@vue/compiler-sfc": "3.2.47",
"@primer/octicons": "19.1.0",
"@vue/compiler-sfc": "3.3.2",
"@webcomponents/custom-elements": "1.6.0",
"add-asset-webpack-plugin": "2.0.1",
"ansi-to-html": "0.7.2",
@@ -29,7 +29,7 @@
"esbuild-loader": "3.0.1",
"escape-goat": "4.0.0",
"fast-glob": "3.2.12",
"jquery": "3.6.4",
"jquery": "3.7.0",
"jquery.are-you-sure": "1.9.0",
"katex": "0.16.7",
"license-checker-webpack-plugin": "0.2.1",
@@ -44,30 +44,32 @@
"tippy.js": "6.3.7",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
"vue": "3.2.47",
"vue": "3.3.2",
"vue-bar-graph": "2.0.0",
"vue-loader": "17.1.0",
"vue-loader": "17.1.1",
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.82.0",
"webpack-cli": "5.1.0",
"webpack": "5.82.1",
"webpack-cli": "5.1.1",
"workbox-routing": "6.5.4",
"workbox-strategies": "6.5.4",
"wrap-ansi": "8.1.0"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "3.2.1",
"@playwright/test": "1.33.0",
"@rollup/pluginutils": "5.0.2",
"@stoplight/spectral-cli": "6.6.0",
"@vitejs/plugin-vue": "4.2.1",
"@vitejs/plugin-vue": "4.2.3",
"eslint": "8.40.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jquery": "1.5.1",
"eslint-plugin-no-jquery": "2.7.0",
"eslint-plugin-regexp": "1.14.0",
"eslint-plugin-regexp": "1.15.0",
"eslint-plugin-sonarjs": "0.19.0",
"eslint-plugin-unicorn": "47.0.0",
"eslint-plugin-vue": "9.11.1",
"eslint-plugin-vue": "9.12.0",
"eslint-plugin-wc": "1.5.0",
"jsdom": "22.0.0",
"markdownlint-cli": "0.34.0",
"stylelint": "15.6.1",
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 186 162" class="svg gitea-alpine" width="16" height="16" aria-hidden="true"><g fill="#0d597f"><path d="M67 100.75V81.125L52.875 95.25a41.588 41.588 0 0 0 4.3 2.637c1.35.71 2.612 1.25 3.787 1.676a21.12 21.12 0 0 0 3.275.887c1.006.184 1.926.266 2.763.278m72.25-1.625c.025.02.163.137.416.298.255.163.628.372 1.123.578.494.205 1.111.409 1.85.56.745.152 1.612.252 2.625.252.838 0 1.762-.073 2.775-.25a20.93 20.93 0 0 0 3.3-.873 29.25 29.25 0 0 0 3.837-1.676 41.805 41.805 0 0 0 4.375-2.674l-10.712-10.5-35.5-35.625-15.625 15.625-21-21.625-52.75 52.125c1.55 1.075 3 1.95 4.362 2.675a28.324 28.324 0 0 0 3.838 1.675c1.189.414 2.287.696 3.3.872 1.012.177 1.937.251 2.775.251 1.005 0 1.875-.1 2.625-.252a9.726 9.726 0 0 0 1.85-.561c.495-.205.866-.414 1.121-.577s.393-.278.418-.3l23.875-23.875 8.512-8.162 23.625 23.625 8.238 8.475c.024.021.162.137.417.299.255.162.626.37 1.121.577.495.205 1.113.409 1.85.56.745.153 1.625.253 2.625.253.838 0 1.763-.074 2.775-.25a20.966 20.966 0 0 0 3.3-.874 29.323 29.323 0 0 0 3.838-1.675 41.805 41.805 0 0 0 4.375-2.675l-18.875-18.5 3.525-3.525 16.375 16.375 9.55 9.462m-.204-98.75 46.5 80.625-46.5 80.625H46.05L-.45 81.066 46.05.441z"/><path d="M110.75 77 98.363 64.625l.88-.886 12.476 12.337z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" class="svg gitea-github" width="16" height="16" aria-hidden="true"><linearGradient id="gitea-github__a" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#gitea-github__a)" d="M25.008 56.007c-.003-.368-.006-1.962-.009-3.454l-.003-1.55c-6.729.915-8.358-3.78-8.376-3.83-.934-2.368-2.211-3.045-2.266-3.073l-.124-.072c-.463-.316-1.691-1.157-1.342-2.263.315-.997 1.536-1.1 2.091-1.082 3.074.215 4.63 2.978 4.694 3.095 1.569 2.689 3.964 2.411 5.509 1.844.144-.688.367-1.32.659-1.878-4.956-.879-10.571-3.515-10.571-13.104 0-2.633.82-4.96 2.441-6.929-.362-1.206-.774-3.666.446-6.765l.174-.442.452-.144c.416-.137 2.688-.624 7.359 2.433a24.959 24.959 0 0 1 6.074-.759c2.115.01 4.158.265 6.09.759 4.667-3.058 6.934-2.565 7.351-2.433l.451.145.174.44c1.225 3.098.813 5.559.451 6.766 1.618 1.963 2.438 4.291 2.438 6.929 0 9.591-5.621 12.219-10.588 13.087.563 1.065.868 2.402.868 3.878 0 1.683-.007 7.204-.015 8.402l-2-.014c.008-1.196.015-6.708.015-8.389 0-2.442-.943-3.522-1.35-3.874l-1.73-1.497 2.274-.253c5.205-.578 10.525-2.379 10.525-11.341 0-2.33-.777-4.361-2.31-6.036l-.43-.469.242-.587c.166-.401.894-2.442-.043-5.291-.758.045-2.568.402-5.584 2.447l-.384.259-.445-.123c-1.863-.518-3.938-.796-6.001-.806-2.052.01-4.124.288-5.984.806l-.445.123-.383-.259c-3.019-2.044-4.833-2.404-5.594-2.449-.935 2.851-.206 4.892-.04 5.293l.242.587-.429.469c-1.536 1.681-2.314 3.712-2.314 6.036 0 8.958 5.31 10.77 10.504 11.361l2.252.256-1.708 1.49c-.372.325-1.03 1.112-1.254 2.727l-.075.549-.506.227c-1.321.592-5.839 2.162-8.548-2.485-.015-.025-.544-.945-1.502-1.557.646.639 1.433 1.673 2.068 3.287.066.19 1.357 3.622 7.28 2.339l1.206-.262.012 3.978c.003 1.487.006 3.076.009 3.444l-1.998.014z"/><linearGradient id="gitea-github__b" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#gitea-github__b)" d="M32 58C17.663 58 6 46.337 6 32S17.663 6 32 6s26 11.663 26 26-11.663 26-26 26zm0-50C18.767 8 8 18.767 8 32s10.767 24 24 24 24-10.767 24-24S45.233 8 32 8z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="svg gitea-gitlab" width="16" height="16" aria-hidden="true"><path fill="#e53935" d="m24 43-8-23h16z"/><path fill="#ff7043" d="m24 43 18-23H32z"/><path fill="#e53935" d="m37 5 5 15H32z"/><path fill="#ffa726" d="m24 43 18-23 3 8z"/><path fill="#ff7043" d="M24 43 6 20h10z"/><path fill="#e53935" d="M11 5 6 20h10z"/><path fill="#ffa726" d="M24 43 6 20l-3 8z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="svg gitea-gitlab" width="16" height="16" aria-hidden="true"><path fill="#E24329" d="m31.462 12.779-.045-.115-4.35-11.35a1.137 1.137 0 0 0-.447-.541 1.163 1.163 0 0 0-1.343.071c-.187.15-.322.356-.386.587l-2.94 9.001h-11.9l-2.941-9a1.138 1.138 0 0 0-1.045-.84 1.153 1.153 0 0 0-1.13.72L.579 12.68l-.045.113a8.09 8.09 0 0 0 2.68 9.34l.016.012.038.03 6.635 4.967 3.28 2.484 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484 6.673-4.997.018-.013a8.088 8.088 0 0 0 2.69-9.352Z"/><path fill="#FC6D26" d="m31.462 12.779-.045-.115a14.748 14.748 0 0 0-5.856 2.634l-9.553 7.24A11225.6 11225.6 0 0 0 22.1 27.14l6.673-4.997.019-.013a8.09 8.09 0 0 0 2.67-9.352Z"/><path fill="#FCA326" d="m9.908 27.14 3.275 2.485 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484s-2.835-2.14-6.092-4.603l-6.08 4.603Z"/><path fill="#FC6D26" d="M6.435 15.305A14.712 14.712 0 0 0 .58 12.672l-.045.113a8.09 8.09 0 0 0 2.68 9.347l.016.012.038.03 6.635 4.967 6.105-4.603-9.573-7.233Z"/></svg>

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="24.7 77.83 205.42 76.8" class="svg gitea-go" width="16" height="16" aria-hidden="true"><g style="fill:#00acd7"><path d="M40.2 101.1c-.4 0-.5-.2-.3-.5l2.1-2.7c.2-.3.7-.5 1.1-.5h35.7c.4 0 .5.3.3.6l-1.7 2.6c-.2.3-.7.6-1 .6l-36.2-.1zM25.1 110.3c-.4 0-.5-.2-.3-.5l2.1-2.7c.2-.3.7-.5 1.1-.5h45.6c.4 0 .6.3.5.6l-.8 2.4c-.1.4-.5.6-.9.6l-47.3.1zM49.3 119.5c-.4 0-.5-.3-.3-.6l1.4-2.5c.2-.3.6-.6 1-.6h20c.4 0 .6.3.6.7l-.2 2.4c0 .4-.4.7-.7.7l-21.8-.1zM153.1 99.3c-6.3 1.6-10.6 2.8-16.8 4.4-1.5.4-1.6.5-2.9-1-1.5-1.7-2.6-2.8-4.7-3.8-6.3-3.1-12.4-2.2-18.1 1.5-6.8 4.4-10.3 10.9-10.2 19 .1 8 5.6 14.6 13.5 15.7 6.8.9 12.5-1.5 17-6.6.9-1.1 1.7-2.3 2.7-3.7h-19.3c-2.1 0-2.6-1.3-1.9-3 1.3-3.1 3.7-8.3 5.1-10.9.3-.6 1-1.6 2.5-1.6h36.4c-.2 2.7-.2 5.4-.6 8.1-1.1 7.2-3.8 13.8-8.2 19.6-7.2 9.5-16.6 15.4-28.5 17-9.8 1.3-18.9-.6-26.9-6.6-7.4-5.6-11.6-13-12.7-22.2-1.3-10.9 1.9-20.7 8.5-29.3 7.1-9.3 16.5-15.2 28-17.3 9.4-1.7 18.4-.6 26.5 4.9 5.3 3.5 9.1 8.3 11.6 14.1.6.9.2 1.4-1 1.7z"/><path d="M186.2 154.6c-9.1-.2-17.4-2.8-24.4-8.8-5.9-5.1-9.6-11.6-10.8-19.3-1.8-11.3 1.3-21.3 8.1-30.2 7.3-9.6 16.1-14.6 28-16.7 10.2-1.8 19.8-.8 28.5 5.1 7.9 5.4 12.8 12.7 14.1 22.3 1.7 13.5-2.2 24.5-11.5 33.9-6.6 6.7-14.7 10.9-24 12.8-2.7.5-5.4.6-8 .9zm23.8-40.4c-.1-1.3-.1-2.3-.3-3.3-1.8-9.9-10.9-15.5-20.4-13.3-9.3 2.1-15.3 8-17.5 17.4-1.8 7.8 2 15.7 9.2 18.9 5.5 2.4 11 2.1 16.3-.6 7.9-4.1 12.2-10.5 12.7-19.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 2400 2400" class="svg gitea-openid" width="16" height="16" aria-hidden="true"><path fill="#ff7c00" d="m1270 218.3-173.1 84.3-.7 981.8c-.3 540 .2 981.4 1.1 981l174.5-81.8 172.3-80.8v-984.4c0-541.7-.2-984.7-.4-984.4z"/><path fill="#aaa" d="M981.9 785.5c-425.3 63.2-766.5 264.1-889 523a491.5 491.5 0 0 0-43.6 146c-4.2 29.2-4.7 95-1.2 124 19 152.6 115.2 299.9 273.2 418.8 147.7 111 350.5 196.5 568.6 239.7 59 11.6 179 29 200.5 29 2.3 0 3-23.2 3-109.1v-109.2l-5.1-1-37.9-6a1182 1182 0 0 1-305.4-90.6c-122.2-55.7-225.1-137.7-284.6-226.4-107.5-160.5-81.3-344.3 70-491.3 57-55.5 115.4-95.2 199.5-136.1a1112.6 1112.6 0 0 1 269.4-89.2l29.7-6c3.7-1.2 4-8.6 4-111.5V779.5l-6.3.2a823 823 0 0 0-44.8 5.8zm525 104c0 103 .2 110.4 4.1 111.6l29.5 6a1221.6 1221.6 0 0 1 207.7 61.3A1087.8 1087.8 0 0 1 1862 1123c4.6 3.7 1.4 5.8-88 56-51.1 28.5-93 52.7-93 53.4 0 1.9 671.6 146.8 673.2 145.2 1.2-1.2-45.5-496-47-497.6-.2-.2-38.5 21-85 47.2l-89.6 50.2c-4.2 2-8.8.2-27.9-10.7-130.8-75-289.6-132.2-460.8-166.1a1870.8 1870.8 0 0 0-132.9-21.1c-4 0-4.2 6.7-4.2 110z"/><path fill="#cbaa7c" d="M1094.5 2156.9c0 60.6.3 85.5.5 55 .5-30.2.5-79.9 0-110.3-.2-30.2-.5-5.3-.5 55.3z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-devices" width="16" height="16" aria-hidden="true"><path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75V5a.75.75 0 0 1-1.5 0V2.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25H7A.75.75 0 0 1 7 12h-.268a5.712 5.712 0 0 1-.765 2.5H7A.75.75 0 0 1 7 16H4.5a.75.75 0 0 1-.565-1.243c.772-.885 1.193-1.716 1.292-2.757H1.75A1.75 1.75 0 0 1 0 10.25v-7.5Z"/><path d="M10.75 7h3.5c.967 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-5.5C9 7.784 9.783 7 10.75 7Zm-.25 1.75v5.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-3.5a.25.25 0 0 0-.25.25Z"/></svg>

After

Width:  |  Height:  |  Size: 722 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-move-to-top" width="16" height="16" aria-hidden="true"><path d="M8.53 1.22a.749.749 0 0 0-1.06 0L3.72 4.97a.749.749 0 1 0 1.06 1.06l2.47-2.469v6.689a.75.75 0 0 0 1.5 0V3.561l2.47 2.469a.749.749 0 1 0 1.06-1.06L8.53 1.22ZM3.75 13a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-move-to-top" width="16" height="16" aria-hidden="true"><path d="M3 2.25a.75.75 0 0 1 .75-.75h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 3 2.25Zm5.53 2.97 3.75 3.75a.749.749 0 1 1-1.06 1.06L8.75 7.561v6.689a.75.75 0 0 1-1.5 0V7.561L4.78 10.03a.749.749 0 1 1-1.06-1.06l3.75-3.75a.749.749 0 0 1 1.06 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 364 B

After

Width:  |  Height:  |  Size: 385 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16" class="svg octicon-pin-slash" width="16" height="16" aria-hidden="true"><path d="m2.155.595 13.75 13.75a.749.749 0 1 1-1.06 1.06l-3.311-3.31-.14.588c-.317 1.332-1.972 1.8-2.94.832L6.25 11.311 2.28 15.28a.749.749 0 1 1-1.06-1.06l3.969-3.97-2.204-2.204c-.968-.968-.5-2.623.832-2.94l.588-.14-3.31-3.311a.749.749 0 1 1 1.06-1.06Zm8.134 10.254L5.651 6.211l-1.486.354a.25.25 0 0 0-.119.42l2.731 2.732.003.003.003.003 2.732 2.731a.25.25 0 0 0 .42-.119ZM11.794.984l3.722 3.722a1.75 1.75 0 0 1-.504 2.826l-1.627.751a1.25 1.25 0 0 0-.355.247.749.749 0 1 1-1.06-1.06c.228-.228.494-.414.787-.549l1.627-.751a.25.25 0 0 0 .072-.404l-3.722-3.722a.25.25 0 0 0-.404.072l-.751 1.627a2.726 2.726 0 0 1-.549.787.749.749 0 1 1-1.06-1.06 1.25 1.25 0 0 0 .247-.355l.751-1.627a1.75 1.75 0 0 1 2.826-.504Z"/></svg>

After

Width:  |  Height:  |  Size: 849 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-pin" width="16" height="16" aria-hidden="true"><path d="M4.456.734a1.75 1.75 0 0 1 2.826.504l.613 1.327a3.08 3.08 0 0 0 2.084 1.707l2.454.584c1.332.317 1.8 1.972.832 2.94L11.06 10l3.72 3.72a.748.748 0 0 1-.332 1.265.75.75 0 0 1-.729-.205L10 11.06l-2.204 2.205c-.968.968-2.623.5-2.94-.832l-.584-2.454a3.08 3.08 0 0 0-1.707-2.084l-1.327-.613a1.75 1.75 0 0 1-.504-2.826ZM5.92 1.866a.253.253 0 0 0-.183-.142.251.251 0 0 0-.221.07L1.794 5.516a.251.251 0 0 0-.07.221c.015.08.068.149.142.183l1.328.613A4.582 4.582 0 0 1 5.73 9.63l.584 2.454a.251.251 0 0 0 .42.12l5.47-5.47a.25.25 0 0 0-.12-.42L9.63 5.73a4.583 4.583 0 0 1-3.098-2.537Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-pin" width="16" height="16" aria-hidden="true"><path d="m11.294.984 3.722 3.722a1.75 1.75 0 0 1-.504 2.826l-1.327.613a3.089 3.089 0 0 0-1.707 2.084l-.584 2.454c-.317 1.332-1.972 1.8-2.94.832L5.75 11.311 1.78 15.28a.749.749 0 1 1-1.06-1.06l3.969-3.97-2.204-2.204c-.968-.968-.5-2.623.832-2.94l2.454-.584a3.08 3.08 0 0 0 2.084-1.707l.613-1.327a1.75 1.75 0 0 1 2.826-.504ZM6.283 9.723l2.732 2.731a.25.25 0 0 0 .42-.119l.584-2.454a4.586 4.586 0 0 1 2.537-3.098l1.328-.613a.25.25 0 0 0 .072-.404l-3.722-3.722a.25.25 0 0 0-.404.072l-.613 1.328a4.584 4.584 0 0 1-3.098 2.537l-2.454.584a.25.25 0 0 0-.119.42l2.731 2.732Z"/></svg>

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 699 B

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