mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-29 20:56:19 +01:00
Merge branch 'forgejo' into forgejo
This commit is contained in:
commit
0095dff5e8
868 changed files with 15269 additions and 8574 deletions
|
@ -162,9 +162,6 @@ package "code.gitea.io/gitea/modules/cache"
|
|||
package "code.gitea.io/gitea/modules/charset"
|
||||
func (*BreakWriter).Write
|
||||
|
||||
package "code.gitea.io/gitea/modules/context"
|
||||
func GetPrivateContext
|
||||
|
||||
package "code.gitea.io/gitea/modules/emoji"
|
||||
func ReplaceCodes
|
||||
|
||||
|
@ -192,6 +189,7 @@ package "code.gitea.io/gitea/modules/gitgraph"
|
|||
|
||||
package "code.gitea.io/gitea/modules/gitrepo"
|
||||
func GetBranchCommitID
|
||||
func GetWikiDefaultBranch
|
||||
|
||||
package "code.gitea.io/gitea/modules/graceful"
|
||||
func (*Manager).TerminateContext
|
||||
|
@ -296,7 +294,6 @@ package "code.gitea.io/gitea/modules/translation"
|
|||
|
||||
package "code.gitea.io/gitea/modules/util"
|
||||
func UnsafeStringToBytes
|
||||
func OptionalBoolFromGeneric
|
||||
|
||||
package "code.gitea.io/gitea/modules/util/filebuffer"
|
||||
func CreateFromReader
|
||||
|
@ -316,6 +313,9 @@ package "code.gitea.io/gitea/routers/web/org"
|
|||
func getActionIssues
|
||||
func UpdateIssueProject
|
||||
|
||||
package "code.gitea.io/gitea/services/context"
|
||||
func GetPrivateContext
|
||||
|
||||
package "code.gitea.io/gitea/services/convert"
|
||||
func ToSecret
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.1.0": {},
|
||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
||||
"ghcr.io/devcontainers/features/python:1": {}
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"version": "3.12"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
|
|
|
@ -5,17 +5,26 @@ set -ex
|
|||
end_to_end=$1
|
||||
end_to_end_pr=$2
|
||||
forgejo=$3
|
||||
forgejo_pr=$4
|
||||
forgejo_pr_or_ref=$4
|
||||
|
||||
cd $forgejo
|
||||
full_version=$(make show-version-full)
|
||||
major_version=$(make show-version-major)
|
||||
|
||||
cd $end_to_end
|
||||
date > last-upgrade
|
||||
|
||||
if test -f "$forgejo_pr_or_ref" ; then
|
||||
forgejo_pr=$forgejo_pr_or_ref
|
||||
head_url=$(jq --raw-output .head.repo.html_url < $forgejo_pr)
|
||||
test "$head_url" != null
|
||||
branch=$(jq --raw-output .head.ref < $forgejo_pr)
|
||||
test "$branch" != null
|
||||
cd $end_to_end
|
||||
echo $head_url $branch 7.0.0+0-gitea-1.22.0 > forgejo/sources/1.22
|
||||
date > last-upgrade
|
||||
echo $head_url $branch $full_version > forgejo/sources/$major_version
|
||||
else
|
||||
forgejo_ref=$forgejo_pr_or_ref
|
||||
echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY ${forgejo_ref#refs/heads/} $full_version > forgejo/sources/$major_version
|
||||
fi
|
||||
|
||||
base_url=$(jq --raw-output .base.repo.html_url < $forgejo_pr)
|
||||
test "$base_url" != null
|
||||
test "$GITHUB_RUN_NUMBER"
|
||||
echo $base_url/actions/runs/$GITHUB_RUN_NUMBER/artifacts/forgejo > forgejo/binary-url
|
||||
echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_NUMBER/artifacts/forgejo > forgejo/binary-url
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
# Copyright 2024 The Forgejo Authors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# To modify this workflow:
|
||||
#
|
||||
# - push it to the wip-ci-end-to-end branch on the forgejo repository
|
||||
# otherwise it will not have access to the secrets required to push
|
||||
# the cascading PR
|
||||
#
|
||||
# - once it works, open a pull request for the sake of keeping track
|
||||
# of the change even if the PR won't run it because it will use
|
||||
# whatever is in the default branch instead
|
||||
#
|
||||
# - after it is merged, double check it works by setting the
|
||||
# run-end-to-end-test on a pull request (any pull request will doe
|
||||
#
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'wip-ci-end-to-end'
|
||||
pull_request_target:
|
||||
types:
|
||||
- labeled
|
||||
|
@ -20,9 +38,18 @@ jobs:
|
|||
cat <<'EOF'
|
||||
${{ toJSON(github.event.pull_request.labels.*.name) }}
|
||||
EOF
|
||||
cat <<'EOF'
|
||||
${{ toJSON(github.event) }}
|
||||
EOF
|
||||
|
||||
build:
|
||||
if: ${{ !startsWith(vars.ROLE, 'forgejo-') && github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests') }}
|
||||
if: >
|
||||
!startsWith(vars.ROLE, 'forgejo-') && (
|
||||
github.event_name == 'push' ||
|
||||
(
|
||||
github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests')
|
||||
)
|
||||
)
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'docker.io/node:20-bookworm'
|
||||
|
@ -55,19 +82,29 @@ jobs:
|
|||
path: forgejo
|
||||
|
||||
cascade:
|
||||
if: ${{ !startsWith(vars.ROLE, 'forgejo-') && github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests') }}
|
||||
if: >
|
||||
!startsWith(vars.ROLE, 'forgejo-') && (
|
||||
github.event_name == 'push' ||
|
||||
(
|
||||
github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests')
|
||||
)
|
||||
)
|
||||
needs: [build]
|
||||
runs-on: docker
|
||||
container:
|
||||
image: node:20-bookworm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cascading-pr@v1
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
show-progress: 'false'
|
||||
- uses: actions/cascading-pr@v2
|
||||
with:
|
||||
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
||||
origin-repo: ${{ github.repository }}
|
||||
origin-token: ${{ secrets.END_TO_END_CASCADING_PR_ORIGIN }}
|
||||
origin-pr: ${{ github.event.pull_request.number }}
|
||||
origin-ref: ${{ github.event_name == 'push' && github.event.ref || '' }}
|
||||
destination-url: https://code.forgejo.org
|
||||
destination-fork-repo: cascading-pr/end-to-end
|
||||
destination-repo: forgejo/end-to-end
|
||||
|
|
|
@ -21,8 +21,6 @@ jobs:
|
|||
check-latest: true
|
||||
- run: make deps-backend deps-tools
|
||||
- run: make --always-make -j$(nproc) lint-backend checks-backend # ensure the "go-licenses" make target runs
|
||||
env:
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
frontend-checks:
|
||||
if: ${{ !startsWith(vars.ROLE, 'forgejo-') }}
|
||||
runs-on: docker
|
||||
|
@ -43,8 +41,11 @@ jobs:
|
|||
image: 'docker.io/node:20-bookworm'
|
||||
services:
|
||||
minio:
|
||||
image: 'docker.io/bitnami/minio:2023.8.31'
|
||||
image: bitnami/minio:2024.2.26
|
||||
options: >-
|
||||
--hostname gitea.minio
|
||||
env:
|
||||
MINIO_DOMAIN: minio
|
||||
MINIO_ROOT_USER: 123456
|
||||
MINIO_ROOT_PASSWORD: 12345678
|
||||
steps:
|
||||
|
@ -130,10 +131,10 @@ jobs:
|
|||
image: 'docker.io/node:20-bookworm'
|
||||
services:
|
||||
minio:
|
||||
image: bitnami/minio:2021.3.17
|
||||
image: bitnami/minio:2024.2.26
|
||||
env:
|
||||
MINIO_ACCESS_KEY: 123456
|
||||
MINIO_SECRET_KEY: 12345678
|
||||
MINIO_ROOT_USER: 123456
|
||||
MINIO_ROOT_PASSWORD: 12345678
|
||||
pgsql:
|
||||
image: 'docker.io/postgres:15'
|
||||
env:
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,7 +18,7 @@ _test
|
|||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
__debug_bin
|
||||
__debug_bin*
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
|
|
|
@ -64,6 +64,7 @@ rules:
|
|||
"@stylistic/media-query-list-comma-newline-before": null
|
||||
"@stylistic/media-query-list-comma-space-after": null
|
||||
"@stylistic/media-query-list-comma-space-before": null
|
||||
"@stylistic/named-grid-areas-alignment": null
|
||||
"@stylistic/no-empty-first-line": null
|
||||
"@stylistic/no-eol-whitespace": true
|
||||
"@stylistic/no-extra-semicolons": true
|
||||
|
|
71
Makefile
71
Makefile
|
@ -93,6 +93,14 @@ ifneq ($(STORED_VERSION),)
|
|||
else
|
||||
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//')+${GITEA_COMPATIBILITY}
|
||||
endif
|
||||
FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//')
|
||||
|
||||
show-version-full:
|
||||
@echo ${FORGEJO_VERSION}
|
||||
|
||||
show-version-major:
|
||||
@echo ${FORGEJO_VERSION_MAJOR}
|
||||
|
||||
RELEASE_VERSION ?= ${FORGEJO_VERSION}
|
||||
VERSION ?= ${RELEASE_VERSION}
|
||||
|
||||
|
@ -100,8 +108,10 @@ LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeV
|
|||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||
|
||||
ifeq ($(HAS_GO), yes)
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) $(shell $(GO) list code.gitea.io/gitea/models/forgejo_migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
endif
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
|
||||
|
@ -140,7 +150,9 @@ GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/optio
|
|||
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||
GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
|
||||
|
||||
ifeq ($(HAS_GO), yes)
|
||||
MIGRATION_PACKAGES := $(shell $(GO) list code.gitea.io/gitea/models/migrations/... code.gitea.io/gitea/models/forgejo_migrations/...)
|
||||
endif
|
||||
|
||||
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
|
@ -219,6 +231,8 @@ help:
|
|||
@echo " - checks-frontend check frontend files"
|
||||
@echo " - checks-backend check backend files"
|
||||
@echo " - test test everything"
|
||||
@echo " - show-version-full show the same version as the API endpoint"
|
||||
@echo " - show-version-major show major release number only"
|
||||
@echo " - test-frontend test frontend files"
|
||||
@echo " - test-backend test backend files"
|
||||
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||
|
@ -299,12 +313,8 @@ fmt:
|
|||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: fmt
|
||||
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always $(GO_SOURCES) templates $(WEB_DIRS) \
|
||||
|| (code=$$?; echo "Please run 'make fmt' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
$(TAGS_EVIDENCE):
|
||||
|
@ -325,12 +335,8 @@ generate-forgejo-api: $(FORGEJO_API_SPEC)
|
|||
|
||||
.PHONY: forgejo-api-check
|
||||
forgejo-api-check: generate-forgejo-api
|
||||
@diff=$$(git diff $(FORGEJO_API_SERVER) ; \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make generate-forgejo-api' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always $(FORGEJO_API_SERVER) \
|
||||
|| (code=$$?; echo "Please run 'make generate-forgejo-api' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: forgejo-api-validate
|
||||
forgejo-api-validate:
|
||||
|
@ -347,12 +353,8 @@ $(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
|
|||
|
||||
.PHONY: swagger-check
|
||||
swagger-check: generate-swagger
|
||||
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always '$(SWAGGER_SPEC)' \
|
||||
|| (code=$$?; echo "Please run 'make generate-swagger' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
|
@ -423,11 +425,8 @@ lint-spell-fix:
|
|||
lint-go:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS)
|
||||
$(GO) run $(DEADCODE_PACKAGE) -generated=false -test code.gitea.io/gitea > .cur-deadcode-out
|
||||
@$(DIFF) .deadcode-out .cur-deadcode-out; \
|
||||
if [ $$? -eq 1 ]; then \
|
||||
echo "Please run 'make lint-go-fix' and commit the result"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@$(DIFF) .deadcode-out .cur-deadcode-out \
|
||||
|| (code=$$?; echo "Please run 'make lint-go-fix' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: lint-go-fix
|
||||
lint-go-fix:
|
||||
|
@ -527,12 +526,8 @@ vendor: go.mod go.sum
|
|||
|
||||
.PHONY: tidy-check
|
||||
tidy-check: tidy
|
||||
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make tidy' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always go.mod go.sum $(GO_LICENSE_FILE) \
|
||||
|| (code=$$?; echo "Please run 'make tidy' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: go-licenses
|
||||
go-licenses: $(GO_LICENSE_FILE)
|
||||
|
@ -947,6 +942,7 @@ fomantic:
|
|||
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
||||
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
||||
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
||||
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
|
||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
|
||||
$(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
|
||||
|
@ -970,23 +966,14 @@ svg: node-check | node_modules
|
|||
.PHONY: svg-check
|
||||
svg-check: svg
|
||||
@git add $(SVG_DEST_DIR)
|
||||
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always --cached $(SVG_DEST_DIR) \
|
||||
|| (code=$$?; echo "Please run 'make svg' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: lockfile-check
|
||||
lockfile-check:
|
||||
npm install --package-lock-only
|
||||
@diff=$$(git diff --color=always package-lock.json); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "package-lock.json is inconsistent with package.json"; \
|
||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@git diff --exit-code --color=always package-lock.json \
|
||||
|| (code=$$?; echo "Please run 'npm install --package-lock-only' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
|
|
|
@ -4,6 +4,43 @@ A Forgejo release is published shortly after a Gitea release is published and th
|
|||
|
||||
The Forgejo admin should carefully read the required manual actions before upgrading. A point release (e.g. v1.21.1-0 or v1.21.2-0) does not require manual actions but others might (e.g. v1.20, v1.21).
|
||||
|
||||
## 1.21.7-0
|
||||
|
||||
The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.7-0` release can be reviewed from the command line with:
|
||||
|
||||
```shell
|
||||
$ git clone https://codeberg.org/forgejo/forgejo/
|
||||
$ git -C forgejo log --oneline --no-merges v1.21.6-0..v1.21.7-0
|
||||
```
|
||||
|
||||
This stable release contains bug fixes and a **security fix**.
|
||||
|
||||
* Recommended Action
|
||||
|
||||
We recommend that all Forgejo installations are [upgraded](https://forgejo.org/docs/v1.21/admin/upgrade/) to the latest version as soon as possible.
|
||||
|
||||
* [Forgejo Semantic Version](https://forgejo.org/docs/v1.21/user/semver/)
|
||||
|
||||
The semantic version was updated to `6.0.7+0-gitea-1.21.7`
|
||||
|
||||
* Built with Go 1.21.8
|
||||
|
||||
It [includes vulnerability fixes](https://groups.google.com/g/golang-announce/c/5pwGVUPoMbg).
|
||||
|
||||
* [CVE-2023-45290](https://go.dev/issue/65383) which could lead to memory exhaustion when parsing a multipart form.
|
||||
* [CVE-2023-45289](https://go.dev/issue/65065) which could allow incorrect forwarding of sensitive headers and cookies on HTTP redirect.
|
||||
|
||||
* Security fix
|
||||
|
||||
* The google.golang.org/protobuf module was bumped to version v1.33.0 to fix a bug in the google.golang.org/protobuf/encoding/protojson package which could cause the Unmarshal function to enter an infinite loop when handling some invalid inputs. [Read more in the announcement](https://groups.google.com/g/golang-announce/c/ArQ6CDgtEjY).
|
||||
|
||||
* Bug fixes
|
||||
|
||||
The most prominent ones are described here, others can be found in the list of commits included in the release as described above.
|
||||
|
||||
* [Fix tarball/zipball download bug](https://codeberg.org/forgejo/forgejo/commit/8e2c991b35de8c94899ad053e89339cea4538589).
|
||||
* [Ensure `HasIssueContentHistory` takes into account `comment_id`](https://codeberg.org/forgejo/forgejo/commit/8fb027fea5e9525293802d977fd3ee0c374ba9ba).
|
||||
|
||||
## 1.21.6-0
|
||||
|
||||
The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.6-0` release can be reviewed from the command line with:
|
||||
|
|
5
assets/go-licenses.json
generated
5
assets/go-licenses.json
generated
|
@ -34,6 +34,11 @@
|
|||
"path": "dario.cat/mergo/LICENSE",
|
||||
"licenseText": "Copyright (c) 2013 Dario Castañé. All rights reserved.\nCopyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "filippo.io/edwards25519",
|
||||
"path": "filippo.io/edwards25519/LICENSE",
|
||||
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "git.sr.ht/~mariusor/go-xsd-duration",
|
||||
"path": "git.sr.ht/~mariusor/go-xsd-duration/LICENSE",
|
||||
|
|
|
@ -47,7 +47,8 @@ var microcmdUserCreate = &cli.Command{
|
|||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "must-change-password",
|
||||
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
|
||||
Usage: "Set this option to false to prevent forcing the user to change their password after initial login",
|
||||
Value: true,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "random-password-length",
|
||||
|
@ -110,8 +111,7 @@ func runCreateUser(c *cli.Context) error {
|
|||
return errors.New("must set either password or random-password flag")
|
||||
}
|
||||
|
||||
// always default to true
|
||||
changePassword := true
|
||||
changePassword := c.Bool("must-change-password")
|
||||
|
||||
// If this is the first user being created.
|
||||
// Take it as the admin and don't force a password update.
|
||||
|
@ -119,10 +119,6 @@ func runCreateUser(c *cli.Context) error {
|
|||
changePassword = false
|
||||
}
|
||||
|
||||
if c.IsSet("must-change-password") {
|
||||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
restricted := optional.None[bool]()
|
||||
|
||||
if c.IsSet("restricted") {
|
||||
|
|
|
@ -969,6 +969,12 @@ LEVEL = Info
|
|||
;GO_GET_CLONE_URL_PROTOCOL = https
|
||||
;;
|
||||
;; Close issues as long as a commit on any branch marks it as fixed
|
||||
;DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
|
||||
;;
|
||||
;; Allow users to push local repositories to Gitea and have them automatically created for a user or an org
|
||||
;ENABLE_PUSH_CREATE_USER = false
|
||||
;ENABLE_PUSH_CREATE_ORG = false
|
||||
;;
|
||||
;; 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 =
|
||||
;;
|
||||
|
@ -1492,8 +1498,10 @@ LEVEL = Info
|
|||
;DEFAULT_EMAIL_NOTIFICATIONS = enabled
|
||||
;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false
|
||||
;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false
|
||||
;; Disabled features for users, could be "deletion", more features can be disabled in future
|
||||
;; Disabled features for users, could be "deletion", "manage_ssh_keys","manage_gpg_keys" more features can be disabled in future
|
||||
;; - deletion: a user cannot delete their own account
|
||||
;; - manage_ssh_keys: a user cannot configure ssh keys
|
||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||
;USER_DISABLED_FEATURES =
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -518,7 +518,10 @@ And the following unique queues:
|
|||
|
||||
- `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
|
||||
- `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations.
|
||||
- `SEND_NOTIFICATION_EMAIL_ON_NEW_USER`: **false**: Send an email to all admins when a new user signs up to inform the admins about this act.
|
||||
- `USER_DISABLED_FEATURES`: **_empty_** Disabled features for users, could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys` and more features can be added in future.
|
||||
- `deletion`: User cannot delete their own account.
|
||||
- `manage_ssh_keys`: User cannot configure ssh keys.
|
||||
- `manage_gpg_keys`: User cannot configure gpg keys.
|
||||
|
||||
## Security (`security`)
|
||||
|
||||
|
@ -829,7 +832,7 @@ Default templates for project boards:
|
|||
## Issue and pull request attachments (`attachment`)
|
||||
|
||||
- `ENABLED`: **true**: Whether issue and pull request attachments are enabled.
|
||||
- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
- `ALLOWED_TYPES`: **.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
- `MAX_SIZE`: **2048**: Maximum size (MB).
|
||||
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
|
||||
- `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
|
||||
|
@ -839,6 +842,10 @@ Default templates for project boards:
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio`
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio`
|
||||
- `auto` Auto detect
|
||||
- `dns` Virtual Host Style bucket lookup
|
||||
- `path` Path style bucket lookup
|
||||
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when STORAGE_TYPE is `minio`
|
||||
- `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio`
|
||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when STORAGE_TYPE is `minio`
|
||||
|
@ -1269,6 +1276,10 @@ is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`.
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
|
||||
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio`
|
||||
- `auto` Auto detect
|
||||
- `dns` Virtual Host Style bucket lookup
|
||||
- `path` Path style bucket lookup
|
||||
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_BASE_PATH`: **lfs/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
||||
|
@ -1284,6 +1295,10 @@ Default storage configuration for attachments, lfs, avatars, repo-avatars, repo-
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
|
||||
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the data only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio`
|
||||
- `auto` Auto detect
|
||||
- `dns` Virtual Host Style bucket lookup
|
||||
- `path` Path style bucket lookup
|
||||
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio`
|
||||
|
@ -1369,6 +1384,10 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`.
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio`
|
||||
- `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio`
|
||||
- `auto` Auto detect
|
||||
- `dns` Virtual Host Style bucket lookup
|
||||
- `path` Path style bucket lookup
|
||||
- `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_BASE_PATH`: **repo-archive/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio`
|
||||
- `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio`
|
||||
|
|
|
@ -497,6 +497,10 @@ Gitea 创建以下非唯一队列:
|
|||
|
||||
- `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**:用户电子邮件通知的默认配置(用户可配置)。选项:enabled、onmention、disabled
|
||||
- `DISABLE_REGULAR_ORG_CREATION`: **false**:禁止普通(非管理员)用户创建组织。
|
||||
- `USER_DISABLED_FEATURES`:**_empty_** 禁用的用户特性,当前允许为空或者 `deletion`,`manage_ssh_keys`, `manage_gpg_keys` 未来可以增加更多设置。
|
||||
- `deletion`: 用户不能通过界面或者API删除他自己。
|
||||
- `manage_ssh_keys`: 用户不能通过界面或者API配置SSH Keys。
|
||||
- `manage_gpg_keys`: 用户不能配置 GPG 密钥。
|
||||
|
||||
## 安全性 (`security`)
|
||||
|
||||
|
@ -778,7 +782,7 @@ Gitea 创建以下非唯一队列:
|
|||
## 工单和合并请求的附件 (`attachment`)
|
||||
|
||||
- `ENABLED`: **true**: 是否允许用户上传附件。
|
||||
- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: 允许的文件扩展名(`.zip`)、mime 类型(`text/plain`)或通配符类型(`image/*`、`audio/*`、`video/*`)的逗号分隔列表。空值或 `*/*` 允许所有类型。
|
||||
- `ALLOWED_TYPES`: **.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: 允许的文件扩展名(`.zip`)、mime 类型(`text/plain`)或通配符类型(`image/*`、`audio/*`、`video/*`)的逗号分隔列表。空值或 `*/*` 允许所有类型。
|
||||
- `MAX_SIZE`: **2048**: 附件的最大限制(MB)。
|
||||
- `MAX_FILES`: **5**: 一次最多上传的附件数量。
|
||||
- `STORAGE_TYPE`: **local**: 附件的存储类型,`local` 表示本地磁盘,`minio` 表示兼容 S3 的对象存储服务,如果未设置将使用默认值 `local` 或其他在 `[storage.xxx]` 中定义的名称。
|
||||
|
@ -788,6 +792,10 @@ Gitea 创建以下非唯一队列:
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID 以连接,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey 以连接,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET`: **gitea**: Minio 存储附件的存储桶,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式, 仅当 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `auto` 自动检测
|
||||
- `dns` 子域名寻址
|
||||
- `path` 路径寻址
|
||||
- `MINIO_LOCATION`: **us-east-1**: Minio 存储桶的位置以创建,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
- `MINIO_BASE_PATH`: **attachments/**: Minio 存储桶上的基本路径,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
- `MINIO_USE_SSL`: **false**: Minio 启用 SSL,仅当 STORAGE_TYPE 为 `minio` 时可用。
|
||||
|
@ -1203,6 +1211,10 @@ ALLOW_DATA_URI_IMAGES = true
|
|||
- `MINIO_ACCESS_KEY_ID`:Minio 的 accessKeyID,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_SECRET_ACCESS_KEY`:Minio 的 secretAccessKey,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET`:**gitea**:用于存储 lfs 的 Minio 桶,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `auto` 自动检测
|
||||
- `dns` 子域名寻址
|
||||
- `path` 路径寻址
|
||||
- `MINIO_LOCATION`:**us-east-1**:创建桶的 Minio 位置,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_BASE_PATH`:**lfs/**:桶上的 Minio 基本路径,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
|
@ -1218,6 +1230,10 @@ ALLOW_DATA_URI_IMAGES = true
|
|||
- `MINIO_ACCESS_KEY_ID`:Minio 的 accessKeyID,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_SECRET_ACCESS_KEY`:Minio 的 secretAccessKey,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET`:**gitea**:用于存储数据的 Minio 桶,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `auto` 自动检测
|
||||
- `dns` 子域名寻址
|
||||
- `path` 路径寻址
|
||||
- `MINIO_LOCATION`:**us-east-1**:创建桶的 Minio 位置,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `MINIO_INSECURE_SKIP_VERIFY`:**false**:Minio 跳过 SSL 验证,仅在 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
|
@ -1301,6 +1317,10 @@ MINIO_INSECURE_SKIP_VERIFY = false
|
|||
- `MINIO_ACCESS_KEY_ID`: Minio的accessKeyID,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
- `MINIO_SECRET_ACCESS_KEY`: Minio的secretAccessKey,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
- `MINIO_BUCKET`: **gitea**:用于存储归档的Minio存储桶,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。
|
||||
- `auto` 自动检测
|
||||
- `dns` 子域名寻址
|
||||
- `path` 路径寻址
|
||||
- `MINIO_LOCATION`: **us-east-1**:用于创建存储桶的Minio位置,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
- `MINIO_BASE_PATH`: **repo-archive/**:存储桶上的Minio基本路径,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
- `MINIO_USE_SSL`: **false**:启用Minio的SSL,仅在`STORAGE_TYPE`为`minio`时可用。
|
||||
|
|
|
@ -222,9 +222,9 @@ Please check [Gitea's logs](administration/logging-config.md) for error messages
|
|||
<a href="{{.Link}}">{{.Repo}}#{{.Issue.Index}}</a>.
|
||||
</p>
|
||||
{{if not (eq .Body "")}}
|
||||
<h3>Message content:</h3>
|
||||
<h3>Message content</h3>
|
||||
<hr>
|
||||
{{.Body | Str2html}}
|
||||
{{.Body}}
|
||||
{{end}}
|
||||
</p>
|
||||
<hr>
|
||||
|
@ -245,7 +245,7 @@ This template produces something along these lines:
|
|||
|
||||
> [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#).
|
||||
>
|
||||
> #### Message content:
|
||||
> #### Message content
|
||||
>
|
||||
> \_********************************\_********************************
|
||||
>
|
||||
|
@ -260,19 +260,19 @@ The template system contains several functions that can be used to further proce
|
|||
the messages. Here's a list of some of them:
|
||||
|
||||
| Name | Parameters | Available | Usage |
|
||||
| ---------------- | ----------- | --------- | --------------------------------------------------------------------------- |
|
||||
| ---------------- | ----------- | --------- | ------------------------------------------------------------------- |
|
||||
| `AppUrl` | - | Any | Gitea's URL |
|
||||
| `AppName` | - | Any | Set from `app.ini`, usually "Gitea" |
|
||||
| `AppDomain` | - | Any | Gitea's host name |
|
||||
| `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed |
|
||||
| `Str2html` | string | Body only | Sanitizes text by removing any HTML tags from it. |
|
||||
| `Safe` | string | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. |
|
||||
| `SanitizeHTML` | string | Body only | Sanitizes text by removing any dangerous HTML tags from it |
|
||||
| `SafeHTML` | string | Body only | Takes the input as HTML, can be used for outputing raw HTML content |
|
||||
|
||||
These are _functions_, not metadata, so they have to be used:
|
||||
|
||||
```html
|
||||
Like this: {{Str2html "Escape<my>text"}}
|
||||
Or this: {{"Escape<my>text" | Str2html}}
|
||||
Like this: {{SanitizeHTML "Escape<my>text"}}
|
||||
Or this: {{"Escape<my>text" | SanitizeHTML}}
|
||||
Or this: {{AppUrl}}
|
||||
But not like this: {{.AppUrl}}
|
||||
```
|
||||
|
|
|
@ -207,7 +207,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
|
|||
{{if not (eq .Body "")}}
|
||||
<h3>消息内容:</h3>
|
||||
<hr>
|
||||
{{.Body | Str2html}}
|
||||
{{.Body}}
|
||||
{{end}}
|
||||
</p>
|
||||
<hr>
|
||||
|
@ -228,7 +228,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
|
|||
|
||||
> [@rhonda](#)(Rhonda Myers)更新了 [mike/stuff#38](#)。
|
||||
>
|
||||
> #### 消息内容:
|
||||
> #### 消息内容
|
||||
>
|
||||
> \_********************************\_********************************
|
||||
>
|
||||
|
@ -243,19 +243,19 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
|
|||
模板系统包含一些函数,可用于进一步处理和格式化消息。以下是其中一些函数的列表:
|
||||
|
||||
| 函数名 | 参数 | 可用于 | 用法 |
|
||||
| ----------------- | ----------- | ------------ | --------------------------------------------------------------------------------- |
|
||||
|------------------| ----------- | ------------ | ------------------------------ |
|
||||
| `AppUrl` | - | 任何地方 | Gitea 的 URL |
|
||||
| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" |
|
||||
| `AppDomain` | - | 任何地方 | Gitea 的主机名 |
|
||||
| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 |
|
||||
| `Str2html` | string | 仅正文部分 | 通过删除其中的 HTML 标签对文本进行清理 |
|
||||
| `Safe` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 |
|
||||
| `SanitizeHTML` | string | 仅正文部分 | 通过删除其中的危险 HTML 标签对文本进行清理 |
|
||||
| `SafeHTML` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于输出原始的 HTML 内容 |
|
||||
|
||||
这些都是 _函数_,而不是元数据,因此必须按以下方式使用:
|
||||
|
||||
```html
|
||||
像这样使用: {{Str2html "Escape<my>text"}}
|
||||
或者这样使用: {{"Escape<my>text" | Str2html}}
|
||||
像这样使用: {{SanitizeHTML "Escape<my>text"}}
|
||||
或者这样使用: {{"Escape<my>text" | SanitizeHTML}}
|
||||
或者这样使用: {{AppUrl}}
|
||||
但不要像这样使用: {{.AppUrl}}
|
||||
```
|
||||
|
|
|
@ -47,7 +47,7 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h
|
|||
9. Avoid unnecessary `!important` in CSS, add comments to explain why it's necessary if it can't be avoided.
|
||||
10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event.
|
||||
11. Custom event names are recommended to use `ce-` prefix.
|
||||
12. Gitea's tailwind-style CSS classes use `gt-` prefix (`gt-relative`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
|
||||
12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-df`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
|
||||
13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided.
|
||||
|
||||
### Accessibility / ARIA
|
||||
|
|
|
@ -34,7 +34,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。
|
|||
|
||||
我们推荐使用[Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html)和[Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)。
|
||||
|
||||
## Gitea 特定准则:
|
||||
## Gitea 特定准则
|
||||
|
||||
1. 每个功能(Fomantic-UI/jQuery 模块)应放在单独的文件/目录中。
|
||||
2. HTML 的 id 和 class 应使用 kebab-case,最好包含2-3个与功能相关的关键词。
|
||||
|
@ -47,7 +47,8 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。
|
|||
9. 避免在 CSS 中使用不必要的`!important`,如果无法避免,添加注释解释为什么需要它。
|
||||
10. 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。
|
||||
11. 推荐使用自定义事件名称前缀`ce-`。
|
||||
12. Gitea 的 tailwind-style CSS 类使用`gt-`前缀(`gt-relative`),而 Gitea 自身的私有框架级 CSS 类使用`g-`前缀(`g-modal-confirm`)。
|
||||
12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-df`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
|
||||
13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。
|
||||
|
||||
### 可访问性 / ARIA
|
||||
|
||||
|
@ -64,18 +65,21 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari
|
|||
|
||||
* Vue + Vanilla JS
|
||||
* Fomantic-UI(jQuery)
|
||||
* htmx (部分页面重新加载其他静态组件)
|
||||
* Vanilla JS
|
||||
|
||||
不推荐的实现方式:
|
||||
|
||||
* Vue + Fomantic-UI(jQuery)
|
||||
* jQuery + Vanilla JS
|
||||
* htmx + 任何其他需要大量 JavaScript 代码或不必要的功能,如 htmx 脚本 (`hx-on`)
|
||||
|
||||
为了保持界面一致,Vue 组件可以使用 Fomantic-UI 的 CSS 类。
|
||||
尽管不建议混合使用不同的框架,
|
||||
我们使用 htmx 进行简单的交互。您可以在此 [PR](https://github.com/go-gitea/gitea/pull/28908) 中查看一个简单交互的示例,其中应使用 htmx。如果您需要更高级的反应性,请不要使用 htmx,请使用其他框架(Vue/Vanilla JS)。
|
||||
但如果混合使用是必要的,并且代码设计良好且易于维护,也可以工作。
|
||||
|
||||
### async 函数
|
||||
### `async` 函数
|
||||
|
||||
只有当函数内部存在`await`调用或返回`Promise`时,才将函数标记为`async`。
|
||||
|
||||
|
@ -91,6 +95,12 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari
|
|||
这是有意为之的,我们想调用异步函数并忽略Promise。
|
||||
一些 lint 规则和 IDE 也会在未处理返回的 Promise 时发出警告。
|
||||
|
||||
### 获取数据
|
||||
|
||||
要获取数据,请使用`modules/fetch.js`中的包装函数`GET`、`POST`等。他们
|
||||
接受内容的`data`选项,将自动设置 CSRF 令牌并返回
|
||||
[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)。
|
||||
|
||||
### HTML 属性和 dataset
|
||||
|
||||
禁止使用`dataset`,它的驼峰命名行为使得搜索属性变得困难。
|
||||
|
@ -132,3 +142,7 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari
|
|||
### Vue3 和 JSX
|
||||
|
||||
Gitea 现在正在使用 Vue3。我们决定不引入 JSX,以保持 HTML 代码和 JavaScript 代码分离。
|
||||
|
||||
### UI示例
|
||||
|
||||
Gitea 使用一些自制的 UI 元素并自定义其他元素,以将它们更好地集成到通用 UI 方法中。当在开发模式(`RUN_MODE=dev`)下运行 Gitea 时,在 `http(s)://your-gitea-url:port/devtest` 下会提供一个包含一些标准化 UI 示例的页面。
|
||||
|
|
|
@ -135,6 +135,12 @@ body:
|
|||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
# some markdown that will only be visible once the issue has been created
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue was created by an issue **template** :)
|
||||
visible: [content]
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
|
@ -186,11 +192,16 @@ body:
|
|||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- label: I have also read the CONTRIBUTION.MD
|
||||
required: true
|
||||
visible: [form]
|
||||
- label: This is a TODO only visible after issue creation
|
||||
visible: [content]
|
||||
```
|
||||
|
||||
### Markdown
|
||||
|
||||
You can use a `markdown` element to display Markdown in your form that provides extra context to the user, but is not submitted.
|
||||
You can use a `markdown` element to display Markdown in your form that provides extra context to the user, but is not submitted by default.
|
||||
|
||||
Attributes:
|
||||
|
||||
|
@ -198,6 +209,8 @@ Attributes:
|
|||
|-------|--------------------------------------------------------------|----------|--------|---------|--------------|
|
||||
| value | The text that is rendered. Markdown formatting is supported. | Required | String | - | - |
|
||||
|
||||
visible: Default is **[form]**
|
||||
|
||||
### Textarea
|
||||
|
||||
You can use a `textarea` element to add a multi-line text field to your form. Contributors can also attach files in `textarea` fields.
|
||||
|
@ -218,6 +231,8 @@ Validations:
|
|||
|----------|------------------------------------------------------|----------|---------|---------|--------------|
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
|
||||
visible: Default is **[form, content]**
|
||||
|
||||
### Input
|
||||
|
||||
You can use an `input` element to add a single-line text field to your form.
|
||||
|
@ -239,6 +254,8 @@ Validations:
|
|||
| is_number | Prevents form submission until element is filled with a number. | Optional | Boolean | false | - |
|
||||
| regex | Prevents form submission until element is filled with a value that match the regular expression. | Optional | String | - | a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) |
|
||||
|
||||
visible: Default is **[form, content]**
|
||||
|
||||
### Dropdown
|
||||
|
||||
You can use a `dropdown` element to add a dropdown menu in your form.
|
||||
|
@ -258,6 +275,8 @@ Validations:
|
|||
|----------|------------------------------------------------------|----------|---------|---------|--------------|
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
|
||||
visible: Default is **[form, content]**
|
||||
|
||||
### Checkboxes
|
||||
|
||||
You can use the `checkboxes` element to add a set of checkboxes to your form.
|
||||
|
@ -265,7 +284,7 @@ You can use the `checkboxes` element to add a set of checkboxes to your form.
|
|||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------|----------|--------|--------------|--------------|
|
||||
| ----------- | ----------------------------------------------------------------------------------------------------- | -------- | ------ | ------------ | ------------ |
|
||||
| label | A brief description of the expected user input, which is displayed in the form. | Required | String | - | - |
|
||||
| description | A description of the set of checkboxes, which is displayed in the form. Supports Markdown formatting. | Optional | String | Empty String | - |
|
||||
| options | An array of checkboxes that the user can select. For syntax, see below. | Required | Array | - | - |
|
||||
|
@ -273,9 +292,12 @@ Attributes:
|
|||
For each value in the options array, you can set the following keys.
|
||||
|
||||
| Key | Description | Required | Type | Default | Options |
|
||||
|----------|------------------------------------------------------------------------------------------------------------------------------------------|----------|---------|---------|---------|
|
||||
|--------------|------------------------------------------------------------------------------------------------------------------------------------------|----------|--------------|---------|---------|
|
||||
| label | The identifier for the option, which is displayed in the form. Markdown is supported for bold or italic text formatting, and hyperlinks. | Required | String | - | - |
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
| visible | Whether a specific checkbox appears in the form only, in the created issue only, or both. Valid options are "form" and "content". | Optional | String array | false | - |
|
||||
|
||||
visible: Default is **[form, content]**
|
||||
|
||||
## Syntax for issue config
|
||||
|
||||
|
@ -292,14 +314,14 @@ contact_links:
|
|||
### Possible Options
|
||||
|
||||
| Key | Description | Type | Default |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------|--------------------|----------------|
|
||||
|----------------------|-------------------------------------------------------|--------------------|-------------|
|
||||
| blank_issues_enabled | If set to false, the User is forced to use a Template | Boolean | true |
|
||||
| contact_links | Custom Links to show in the Choose Box | Contact Link Array | Empty Array |
|
||||
|
||||
### Contact Link
|
||||
|
||||
| Key | Description | Type | Required |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------|---------|----------|
|
||||
|-------|----------------------------------|--------|----------|
|
||||
| name | the name of your link | String | true |
|
||||
| url | The URL of your Link | String | true |
|
||||
| about | A short description of your Link | String | true |
|
||||
|
|
17
go.mod
17
go.mod
|
@ -45,9 +45,9 @@ require (
|
|||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.0
|
||||
github.com/go-swagger/go-swagger v0.30.5
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0
|
||||
github.com/go-webauthn/webauthn v0.10.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
|
@ -55,7 +55,7 @@ require (
|
|||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/google/go-github/v57 v57.0.0
|
||||
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.1.2
|
||||
github.com/gorilla/sessions v1.2.2
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
|
@ -71,7 +71,7 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.78.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.19
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/meilisearch/meilisearch-go v0.26.1
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
|
@ -109,7 +109,7 @@ require (
|
|||
golang.org/x/text v0.14.0
|
||||
golang.org/x/tools v0.17.0
|
||||
google.golang.org/grpc v1.60.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
@ -123,9 +123,10 @@ require (
|
|||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/ClickHouse/ch-go v0.61.1 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.17.1 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
|
@ -238,7 +239,7 @@ require (
|
|||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/paulmach/orb v0.11.0 // indirect
|
||||
github.com/paulmach/orb v0.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
|
@ -298,7 +299,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
|||
|
||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.2.51
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.259.1
|
||||
|
||||
replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5
|
||||
|
||||
|
|
34
go.sum
34
go.sum
|
@ -48,10 +48,12 @@ connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4Aj
|
|||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||
gitea.com/gitea/act v0.2.51 h1:gXc/B4OlTciTTzAx9cmNyw04n2SDO7exPjAsR5Idu+c=
|
||||
gitea.com/gitea/act v0.2.51/go.mod h1:CoaX2053jqBlD6JMgu4d4UgFL/rp2I14Kt5mMqcs0Z0=
|
||||
gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw=
|
||||
gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8=
|
||||
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo=
|
||||
gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||
|
@ -80,8 +82,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ClickHouse/ch-go v0.61.1 h1:j5rx3qnvcnYjhnP1IdXE/vdIRQiqgwAzyqOaasA6QCw=
|
||||
github.com/ClickHouse/ch-go v0.61.1/go.mod h1:myxt/JZgy2BYHFGQqzmaIpbfr5CMbs3YHVULaWQj5YU=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.17.1 h1:ZCmAYWpu75IyEi7+Yrs/uaAjiCGY5wfW5kXo64exkX4=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.17.1/go.mod h1:rkGTvFDTLqLIm0ma+13xmcCfr/08Gvs7KmFt1tgiWHQ=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0 h1:O1LicIeg2JS2V29fKRH4+yT3f6jvvcJBm506dpVQ4mQ=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0/go.mod h1:ztQvX6wm7kAbhJslS87EXEhOVNY/TObXwyURnGju5FQ=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
|
@ -342,8 +344,8 @@ github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XH
|
|||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-swagger/go-swagger v0.30.5 h1:SQ2+xSonWjjoEMOV5tcOnZJVlfyUfCBhGQGArS1b9+U=
|
||||
github.com/go-swagger/go-swagger v0.30.5/go.mod h1:cWUhSyCNqV7J1wkkxfr5QmbcnCewetCdvEXqgPvbc/Q=
|
||||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0=
|
||||
|
@ -351,8 +353,8 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.m
|
|||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0 h1:938g5V+GWLVejm3Hc+nWCuEXRlcglZDDlN/t1gWzcSY=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0/go.mod h1:cdsKD2ApFBjdog9jRsz6EJqF+LClq/hrwE9K/1Dzo4s=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0 h1:BrBwN7AuC+74g5qtk9D59TLGOaEa8Bw1WmIsf+SyzWc=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0/go.mod h1:z8RoleoNtibi6Ar8ziCW7e6PQ+jWiqbUWvuv8AMe4lo=
|
||||
github.com/go-webauthn/webauthn v0.10.0 h1:yuW2e1tXnRAwAvKrR4q4LQmc6XtCMH639/ypZGhZCwk=
|
||||
github.com/go-webauthn/webauthn v0.10.0/go.mod h1:l0NiauXhL6usIKqNLCUM3Qir43GK7ORg8ggold0Uv/Y=
|
||||
github.com/go-webauthn/x v0.1.6 h1:QNAX+AWeqRt9loE8mULeWJCqhVG5D/jvdmJ47fIWCkQ=
|
||||
|
@ -455,8 +457,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
@ -607,8 +609,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
|||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/meilisearch/meilisearch-go v0.26.1 h1:3bmo2uLijX7kvBmiZ9LupVfC95TFcRJDgrRTzbOoE4A=
|
||||
github.com/meilisearch/meilisearch-go v0.26.1/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
|
@ -679,8 +681,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
|||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/paulmach/orb v0.11.0 h1:JfVXJUBeH9ifc/OrhBY0lL16QsmPgpCHMlqSSYhcgAA=
|
||||
github.com/paulmach/orb v0.11.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
||||
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
|
||||
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
|
@ -1239,8 +1241,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
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/optional"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -159,7 +160,7 @@ type FindRunnerOptions struct {
|
|||
OwnerID int64
|
||||
Sort string
|
||||
Filter string
|
||||
IsOnline util.OptionalBool
|
||||
IsOnline optional.Option[bool]
|
||||
WithAvailable bool // not only runners belong to, but also runners can be used
|
||||
}
|
||||
|
||||
|
@ -186,11 +187,13 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
|
|||
cond = cond.And(builder.Like{"name", opts.Filter})
|
||||
}
|
||||
|
||||
if opts.IsOnline.IsTrue() {
|
||||
if opts.IsOnline.Has() {
|
||||
if opts.IsOnline.Value() {
|
||||
cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||
} else if opts.IsOnline.IsFalse() {
|
||||
} else {
|
||||
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||
}
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -82,3 +83,35 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error)
|
|||
})
|
||||
return count != 0, err
|
||||
}
|
||||
|
||||
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
||||
variables := map[string]string{}
|
||||
|
||||
// Global
|
||||
globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
|
||||
if err != nil {
|
||||
log.Error("find global variables: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Org / User level
|
||||
ownerVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID})
|
||||
if err != nil {
|
||||
log.Error("find variables of org: %d, error: %v", run.Repo.OwnerID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Repo level
|
||||
repoVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{RepoID: run.RepoID})
|
||||
if err != nil {
|
||||
log.Error("find variables of repo: %d, error: %v", run.RepoID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Level precedence: Repo > Org / User > Global
|
||||
for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) {
|
||||
variables[v.Name] = v.Data
|
||||
}
|
||||
|
||||
return variables, nil
|
||||
}
|
||||
|
|
|
@ -227,8 +227,8 @@ func (a *Action) ShortActUserName(ctx context.Context) string {
|
|||
return base.EllipsisString(a.GetActUserName(ctx), 20)
|
||||
}
|
||||
|
||||
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
|
||||
func (a *Action) GetDisplayName(ctx context.Context) string {
|
||||
// GetActDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
|
||||
func (a *Action) GetActDisplayName(ctx context.Context) string {
|
||||
if setting.UI.DefaultShowFullName {
|
||||
trimmedFullName := strings.TrimSpace(a.GetActFullName(ctx))
|
||||
if len(trimmedFullName) > 0 {
|
||||
|
@ -238,8 +238,8 @@ func (a *Action) GetDisplayName(ctx context.Context) string {
|
|||
return a.ShortActUserName(ctx)
|
||||
}
|
||||
|
||||
// GetDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
|
||||
func (a *Action) GetDisplayNameTitle(ctx context.Context) string {
|
||||
// GetActDisplayNameTitle gets the action's display name used for the title (tooltip) based on DEFAULT_SHOW_FULL_NAME
|
||||
func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
|
||||
if setting.UI.DefaultShowFullName {
|
||||
return a.ShortActUserName(ctx)
|
||||
}
|
||||
|
@ -395,10 +395,14 @@ func (a *Action) GetCreate() time.Time {
|
|||
return a.CreatedUnix.AsTime()
|
||||
}
|
||||
|
||||
// GetIssueInfos returns a list of issues associated with
|
||||
// the action.
|
||||
// GetIssueInfos returns a list of associated information with the action.
|
||||
func (a *Action) GetIssueInfos() []string {
|
||||
return strings.SplitN(a.Content, "|", 3)
|
||||
// make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length
|
||||
ret := strings.SplitN(a.Content, "|", 3)
|
||||
for len(ret) < 3 {
|
||||
ret = append(ret, "")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetIssueTitle returns the title of first issue associated with the action.
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -243,14 +244,14 @@ func CreateSource(ctx context.Context, source *Source) error {
|
|||
|
||||
type FindSourcesOptions struct {
|
||||
db.ListOptions
|
||||
IsActive util.OptionalBool
|
||||
IsActive optional.Option[bool]
|
||||
LoginType Type
|
||||
}
|
||||
|
||||
func (opts FindSourcesOptions) ToConds() builder.Cond {
|
||||
conds := builder.NewCond()
|
||||
if !opts.IsActive.IsNone() {
|
||||
conds = conds.And(builder.Eq{"is_active": opts.IsActive.IsTrue()})
|
||||
if opts.IsActive.Has() {
|
||||
conds = conds.And(builder.Eq{"is_active": opts.IsActive.Value()})
|
||||
}
|
||||
if opts.LoginType != NoType {
|
||||
conds = conds.And(builder.Eq{"`type`": opts.LoginType})
|
||||
|
@ -262,7 +263,7 @@ func (opts FindSourcesOptions) ToConds() builder.Cond {
|
|||
// source of type LoginSSPI
|
||||
func IsSSPIEnabled(ctx context.Context) bool {
|
||||
exist, err := db.Exist[Source](ctx, FindSourcesOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
IsActive: optional.Some(true),
|
||||
LoginType: SSPI,
|
||||
}.ToConds())
|
||||
if err != nil {
|
||||
|
|
|
@ -166,8 +166,7 @@ func preprocessDatabaseCollation(x *xorm.Engine) {
|
|||
|
||||
// try to alter database collation to expected if the database is empty, it might fail in some cases (and it isn't necessary to succeed)
|
||||
// at the moment, there is no "altering" solution for MSSQL, site admin should manually change the database collation
|
||||
// and there is a bug https://github.com/go-testfixtures/testfixtures/pull/182 mssql: Invalid object name 'information_schema.tables'.
|
||||
if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 && x.Dialect().URI().DBType == schemas.MYSQL {
|
||||
if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 {
|
||||
if err = alterDatabaseCollation(x, r.ExpectedCollation); err != nil {
|
||||
log.Error("Failed to change database collation to %q: %v", r.ExpectedCollation, err)
|
||||
} else {
|
||||
|
|
|
@ -17,3 +17,22 @@
|
|||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
-
|
||||
id: 792
|
||||
title: "update actions"
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
workflow_id: "artifact.yaml"
|
||||
index: 188
|
||||
trigger_user_id: 1
|
||||
ref: "refs/heads/master"
|
||||
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
|
||||
event: "push"
|
||||
is_fork_pull_request: 0
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
created: 1683636108
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
|
|
|
@ -12,3 +12,17 @@
|
|||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 193
|
||||
run_id: 792
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
name: job_2
|
||||
attempt: 1
|
||||
job_id: job_2
|
||||
task_id: 48
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
|
|
|
@ -18,3 +18,23 @@
|
|||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
-
|
||||
id: 48
|
||||
job_id: 193
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 6 # 6 is the status code for "running", running task can upload artifacts
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
token_hash: ffffcfffffffbffffffffffffffffefffffffafffffffffffffffffffffffffffffdffffffffffffffffffffffffffffffff
|
||||
token_salt: ffffffffff
|
||||
token_last_eight: ffffffff
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
|
|
|
@ -3,3 +3,35 @@
|
|||
hook_id: 1
|
||||
uuid: uuid1
|
||||
is_delivered: true
|
||||
is_succeed: false
|
||||
request_content: >
|
||||
{
|
||||
"url": "/matrix-delivered",
|
||||
"http_method":"PUT",
|
||||
"headers": {
|
||||
"X-Head": "42"
|
||||
},
|
||||
"body": "{}"
|
||||
}
|
||||
|
||||
-
|
||||
id: 2
|
||||
hook_id: 1
|
||||
uuid: uuid2
|
||||
is_delivered: false
|
||||
|
||||
-
|
||||
id: 3
|
||||
hook_id: 1
|
||||
uuid: uuid3
|
||||
is_delivered: true
|
||||
is_succeed: true
|
||||
payload_content: '{"key":"value"}' # legacy task, payload saved in payload_content (and not in request_content)
|
||||
request_content: >
|
||||
{
|
||||
"url": "/matrix-success",
|
||||
"http_method":"PUT",
|
||||
"headers": {
|
||||
"X-Head": "42"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,11 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
|
|||
return &branch, nil
|
||||
}
|
||||
|
||||
func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) {
|
||||
branches := make([]*Branch, 0, len(branchNames))
|
||||
return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches)
|
||||
}
|
||||
|
||||
func AddBranches(ctx context.Context, branches []*Branch) error {
|
||||
for _, branch := range branches {
|
||||
if _, err := db.GetEngine(ctx).Insert(branch); err != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ package issues
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
|
@ -21,6 +22,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
@ -260,7 +262,7 @@ type Comment struct {
|
|||
Line int64 // - previous line / + proposed line
|
||||
TreePath string
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
|
||||
// Path represents the 4 lines of code cemented by this comment
|
||||
Patch string `xorm:"-"`
|
||||
|
@ -1043,8 +1045,8 @@ type FindCommentsOptions struct {
|
|||
TreePath string
|
||||
Type CommentType
|
||||
IssueIDs []int64
|
||||
Invalidated util.OptionalBool
|
||||
IsPull util.OptionalBool
|
||||
Invalidated optional.Option[bool]
|
||||
IsPull optional.Option[bool]
|
||||
}
|
||||
|
||||
// ToConds implements FindOptions interface
|
||||
|
@ -1076,11 +1078,11 @@ func (opts FindCommentsOptions) ToConds() builder.Cond {
|
|||
if len(opts.TreePath) > 0 {
|
||||
cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath})
|
||||
}
|
||||
if !opts.Invalidated.IsNone() {
|
||||
cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()})
|
||||
if opts.Invalidated.Has() {
|
||||
cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.Value()})
|
||||
}
|
||||
if opts.IsPull != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.IsTrue()})
|
||||
if opts.IsPull.Has() {
|
||||
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.Value()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
@ -1089,7 +1091,7 @@ func (opts FindCommentsOptions) ToConds() builder.Cond {
|
|||
func FindComments(ctx context.Context, opts *FindCommentsOptions) (CommentList, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||
if opts.RepoID > 0 || opts.IsPull != util.OptionalBoolNone {
|
||||
if opts.RepoID > 0 || opts.IsPull.Has() {
|
||||
sess.Join("INNER", "issue", "issue.id = comment.issue_id")
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package issues
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"slices"
|
||||
|
||||
|
@ -105,7 +106,7 @@ type Issue struct {
|
|||
OriginalAuthorID int64 `xorm:"index"`
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
@ -34,8 +34,8 @@ type IssuesOptions struct { //nolint
|
|||
MilestoneIDs []int64
|
||||
ProjectID int64
|
||||
ProjectBoardID int64
|
||||
IsClosed util.OptionalBool
|
||||
IsPull util.OptionalBool
|
||||
IsClosed optional.Option[bool]
|
||||
IsPull optional.Option[bool]
|
||||
LabelIDs []int64
|
||||
IncludedLabelNames []string
|
||||
ExcludedLabelNames []string
|
||||
|
@ -46,7 +46,7 @@ type IssuesOptions struct { //nolint
|
|||
UpdatedBeforeUnix int64
|
||||
// prioritize issues from this repo
|
||||
PriorityRepoID int64
|
||||
IsArchived util.OptionalBool
|
||||
IsArchived optional.Option[bool]
|
||||
Org *organization.Organization // issues permission scope
|
||||
Team *organization.Team // issues permission scope
|
||||
User *user_model.User // issues permission scope
|
||||
|
@ -217,8 +217,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
|
|||
|
||||
applyRepoConditions(sess, opts)
|
||||
|
||||
if !opts.IsClosed.IsNone() {
|
||||
sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
|
||||
if opts.IsClosed.Has() {
|
||||
sess.And("issue.is_closed=?", opts.IsClosed.Value())
|
||||
}
|
||||
|
||||
if opts.AssigneeID > 0 {
|
||||
|
@ -260,21 +260,18 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
|
|||
|
||||
applyProjectBoardCondition(sess, opts)
|
||||
|
||||
switch opts.IsPull {
|
||||
case util.OptionalBoolTrue:
|
||||
sess.And("issue.is_pull=?", true)
|
||||
case util.OptionalBoolFalse:
|
||||
sess.And("issue.is_pull=?", false)
|
||||
if opts.IsPull.Has() {
|
||||
sess.And("issue.is_pull=?", opts.IsPull.Value())
|
||||
}
|
||||
|
||||
if opts.IsArchived != util.OptionalBoolNone {
|
||||
sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()})
|
||||
if opts.IsArchived.Has() {
|
||||
sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.Value()})
|
||||
}
|
||||
|
||||
applyLabelsCondition(sess, opts)
|
||||
|
||||
if opts.User != nil {
|
||||
sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()))
|
||||
sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()))
|
||||
}
|
||||
|
||||
return sess
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
@ -170,11 +169,8 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6
|
|||
applyReviewedCondition(sess, opts.ReviewedID)
|
||||
}
|
||||
|
||||
switch opts.IsPull {
|
||||
case util.OptionalBoolTrue:
|
||||
sess.And("issue.is_pull=?", true)
|
||||
case util.OptionalBoolFalse:
|
||||
sess.And("issue.is_pull=?", false)
|
||||
if opts.IsPull.Has() {
|
||||
sess.And("issue.is_pull=?", opts.IsPull.Value())
|
||||
}
|
||||
|
||||
return sess
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/label"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -126,7 +127,7 @@ func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) {
|
|||
counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{
|
||||
RepoIDs: []int64{repoID},
|
||||
LabelIDs: []int64{labelID},
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
IsClosed: optional.Some(false),
|
||||
})
|
||||
|
||||
for _, count := range counts {
|
||||
|
|
|
@ -6,10 +6,12 @@ package issues
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -48,7 +50,7 @@ type Milestone struct {
|
|||
Repo *repo_model.Repository `xorm:"-"`
|
||||
Name string
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
IsClosed bool
|
||||
NumIssues int
|
||||
NumClosedIssues int
|
||||
|
@ -313,7 +315,7 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error {
|
|||
}
|
||||
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 {
|
|||
type FindMilestoneOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
IsClosed util.OptionalBool
|
||||
IsClosed optional.Option[bool]
|
||||
Name string
|
||||
SortType string
|
||||
RepoCond builder.Cond
|
||||
|
@ -40,8 +40,8 @@ func (opts FindMilestoneOptions) ToConds() builder.Cond {
|
|||
if opts.RepoID != 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.IsClosed != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.IsTrue()})
|
||||
if opts.IsClosed.Has() {
|
||||
cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()})
|
||||
}
|
||||
if opts.RepoCond != nil && opts.RepoCond.IsValid() {
|
||||
cond = cond.And(builder.In("repo_id", builder.Select("id").From("repository").Where(opts.RepoCond)))
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -39,10 +39,10 @@ func TestGetMilestoneByRepoID(t *testing.T) {
|
|||
func TestGetMilestonesByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
test := func(repoID int64, state api.StateType) {
|
||||
var isClosed util.OptionalBool
|
||||
var isClosed optional.Option[bool]
|
||||
switch state {
|
||||
case api.StateClosed, api.StateOpen:
|
||||
isClosed = util.OptionalBoolOf(state == api.StateClosed)
|
||||
isClosed = optional.Some(state == api.StateClosed)
|
||||
}
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
|
||||
|
@ -84,7 +84,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
|
|||
|
||||
milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
|
||||
RepoID: unittest.NonexistentID,
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
IsClosed: optional.Some(false),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, milestones, 0)
|
||||
|
@ -101,7 +101,7 @@ func TestGetMilestones(t *testing.T) {
|
|||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
IsClosed: optional.Some(false),
|
||||
SortType: sortType,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
@ -118,7 +118,7 @@ func TestGetMilestones(t *testing.T) {
|
|||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
Name: "",
|
||||
SortType: sortType,
|
||||
})
|
||||
|
@ -178,7 +178,7 @@ func TestCountRepoClosedMilestones(t *testing.T) {
|
|||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
|
||||
RepoID: repoID,
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, repo.NumClosedMilestones, count)
|
||||
|
@ -189,7 +189,7 @@ func TestCountRepoClosedMilestones(t *testing.T) {
|
|||
|
||||
count, err := db.Count[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
|
||||
RepoID: unittest.NonexistentID,
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
|
@ -206,7 +206,7 @@ func TestCountMilestonesByRepoIDs(t *testing.T) {
|
|||
|
||||
openCounts, err := issues_model.CountMilestonesMap(db.DefaultContext, issues_model.FindMilestoneOptions{
|
||||
RepoIDs: []int64{1, 2},
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
IsClosed: optional.Some(false),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, repo1OpenCount, openCounts[1])
|
||||
|
@ -215,7 +215,7 @@ func TestCountMilestonesByRepoIDs(t *testing.T) {
|
|||
closedCounts, err := issues_model.CountMilestonesMap(db.DefaultContext,
|
||||
issues_model.FindMilestoneOptions{
|
||||
RepoIDs: []int64{1, 2},
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, repo1ClosedCount, closedCounts[1])
|
||||
|
@ -234,7 +234,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
|
|||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoIDs: []int64{repo1.ID, repo2.ID},
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
IsClosed: optional.Some(false),
|
||||
SortType: sortType,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
@ -252,7 +252,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) {
|
|||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoIDs: []int64{repo1.ID, repo2.ID},
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
IsClosed: optional.Some(true),
|
||||
SortType: sortType,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -891,6 +891,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque
|
|||
return nil
|
||||
}
|
||||
|
||||
if err := pull.LoadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pull.Repo.IsFork {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -901,12 +909,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque
|
|||
}
|
||||
defer repo.Close()
|
||||
|
||||
branch, err := repo.GetDefaultBranch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commit, err := repo.GetBranchCommit(branch)
|
||||
commit, err := repo.GetBranchCommit(pr.BaseRepo.DefaultBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -929,7 +932,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque
|
|||
}
|
||||
// Use the merge base as the base instead of the main branch to avoid problems
|
||||
// if the pull request is out of date with the base branch.
|
||||
changedFiles, err := repo.GetFilesChangedBetween(prInfo.MergeBase, pr.HeadCommitID)
|
||||
changedFiles, err := repo.GetFilesChangedBetween(prInfo.MergeBase, prInfo.HeadCommitID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -68,7 +68,7 @@ type FindReviewOptions struct {
|
|||
IssueID int64
|
||||
ReviewerID int64
|
||||
OfficialOnly bool
|
||||
Dismissed util.OptionalBool
|
||||
Dismissed optional.Option[bool]
|
||||
}
|
||||
|
||||
func (opts *FindReviewOptions) toCond() builder.Cond {
|
||||
|
@ -85,8 +85,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond {
|
|||
if opts.OfficialOnly {
|
||||
cond = cond.And(builder.Eq{"official": true})
|
||||
}
|
||||
if !opts.Dismissed.IsNone() {
|
||||
cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.IsTrue()})
|
||||
if opts.Dismissed.Has() {
|
||||
cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.Value()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -340,7 +341,7 @@ func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) {
|
|||
}
|
||||
|
||||
// GetIssueTotalTrackedTime returns the total tracked time for issues by given conditions.
|
||||
func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool) (int64, error) {
|
||||
func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed optional.Option[bool]) (int64, error) {
|
||||
if len(opts.IssueIDs) <= MaxQueryParameters {
|
||||
return getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs)
|
||||
}
|
||||
|
@ -363,7 +364,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed
|
|||
return accum, nil
|
||||
}
|
||||
|
||||
func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool, issueIDs []int64) (int64, error) {
|
||||
func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed optional.Option[bool], issueIDs []int64) (int64, error) {
|
||||
sumSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session {
|
||||
sess := db.GetEngine(ctx).
|
||||
Table("tracked_time").
|
||||
|
@ -378,8 +379,8 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC
|
|||
}
|
||||
|
||||
session := sumSession(opts, issueIDs)
|
||||
if !isClosed.IsNone() {
|
||||
session = session.And("issue.is_closed = ?", isClosed.IsTrue())
|
||||
if isClosed.Has() {
|
||||
session = session.And("issue.is_closed = ?", isClosed.Value())
|
||||
}
|
||||
return session.SumInt(new(trackedTime), "tracked_time.time")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -120,15 +120,15 @@ func TestTotalTimesForEachUser(t *testing.T) {
|
|||
func TestGetIssueTotalTrackedTime(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolFalse)
|
||||
ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.Some(false))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3682, ttt)
|
||||
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolTrue)
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.Some(true))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, ttt)
|
||||
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolNone)
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, optional.None[bool]())
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3682, ttt)
|
||||
}
|
||||
|
|
|
@ -36,12 +36,14 @@ func Test_DropTableColumns(t *testing.T) {
|
|||
"updated_unix",
|
||||
}
|
||||
|
||||
for i := range columns {
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
|
||||
for i := range columns {
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
|
@ -64,7 +66,6 @@ func Test_DropTableColumns(t *testing.T) {
|
|||
return
|
||||
}
|
||||
for j := range columns[i+1:] {
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
index: 1
|
|
@ -0,0 +1,16 @@
|
|||
- id: 11
|
||||
uuid: uuid11
|
||||
hook_id: 1
|
||||
payload_content: >
|
||||
{"data":"payload"}
|
||||
event_type: create
|
||||
delivered: 1706106005
|
||||
|
||||
- id: 101
|
||||
uuid: uuid101
|
||||
hook_id: 1
|
||||
payload_content: >
|
||||
{"data":"payload"}
|
||||
event_type: create
|
||||
delivered: 1706106006
|
||||
is_delivered: true
|
|
@ -0,0 +1,18 @@
|
|||
- id: 11
|
||||
uuid: uuid11
|
||||
hook_id: 1
|
||||
payload_content: >
|
||||
{"data":"payload"}
|
||||
event_type: create
|
||||
delivered: 1706106005
|
||||
payload_version: 1
|
||||
|
||||
- id: 101
|
||||
uuid: uuid101
|
||||
hook_id: 1
|
||||
payload_content: >
|
||||
{"data":"payload"}
|
||||
event_type: create
|
||||
delivered: 1706106006
|
||||
is_delivered: true
|
||||
payload_version: 1
|
|
@ -0,0 +1,11 @@
|
|||
-
|
||||
id: 1
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
|
||||
issue_id: 1
|
||||
release_id: 0
|
||||
|
||||
-
|
||||
id: 2
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12
|
||||
issue_id: 0
|
||||
release_id: 1
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
repo_id: 1
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
repo_id: 1
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
context_hash: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,5 @@
|
|||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
merge_base: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
||||
merged_commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
sha1: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,3 @@
|
|||
-
|
||||
id: 1
|
||||
commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d
|
|
@ -0,0 +1,4 @@
|
|||
-
|
||||
id: 1
|
||||
description: the badge
|
||||
image_url: https://gitea.com/myimage.png
|
|
@ -560,6 +560,14 @@ var migrations = []Migration{
|
|||
NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
|
||||
// v286 -> v287
|
||||
NewMigration("Add support for SHA256 git repositories", v1_22.AdjustDBForSha256),
|
||||
// v287 -> v288
|
||||
NewMigration("Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges),
|
||||
// v288 -> v289
|
||||
NewMigration("Add user_blocking table", v1_22.AddUserBlockingTable),
|
||||
// v289 -> v290
|
||||
NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch),
|
||||
// v290 -> v291
|
||||
NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -15,7 +15,6 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
|
|||
type Attachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
RepoID int64 `xorm:"INDEX"` // this should not be zero
|
||||
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
|
||||
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
|
||||
UploaderID int64 `xorm:"INDEX DEFAULT 0"`
|
||||
|
@ -44,12 +43,21 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
var issueAttachments []*Attachment
|
||||
err := x.Where("issue_id > 0").Find(&issueAttachments)
|
||||
type NewAttachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
RepoID int64 `xorm:"INDEX"` // this should not be zero
|
||||
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
|
||||
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
|
||||
UploaderID int64 `xorm:"INDEX DEFAULT 0"`
|
||||
}
|
||||
|
||||
var issueAttachments []*NewAttachment
|
||||
err := x.Table("attachment").Where("issue_id > 0").Find(&issueAttachments)
|
||||
assert.NoError(t, err)
|
||||
for _, attach := range issueAttachments {
|
||||
assert.Greater(t, attach.RepoID, 0)
|
||||
assert.Greater(t, attach.IssueID, 0)
|
||||
assert.Greater(t, attach.RepoID, int64(0))
|
||||
assert.Greater(t, attach.IssueID, int64(0))
|
||||
var issue Issue
|
||||
has, err := x.ID(attach.IssueID).Get(&issue)
|
||||
assert.NoError(t, err)
|
||||
|
@ -57,12 +65,12 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
|
|||
assert.EqualValues(t, attach.RepoID, issue.RepoID)
|
||||
}
|
||||
|
||||
var releaseAttachments []*Attachment
|
||||
err = x.Where("release_id > 0").Find(&releaseAttachments)
|
||||
var releaseAttachments []*NewAttachment
|
||||
err = x.Table("attachment").Where("release_id > 0").Find(&releaseAttachments)
|
||||
assert.NoError(t, err)
|
||||
for _, attach := range releaseAttachments {
|
||||
assert.Greater(t, attach.RepoID, 0)
|
||||
assert.Greater(t, attach.IssueID, 0)
|
||||
assert.Greater(t, attach.RepoID, int64(0))
|
||||
assert.Greater(t, attach.ReleaseID, int64(0))
|
||||
var release Release
|
||||
has, err := x.ID(attach.ReleaseID).Get(&release)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -4,10 +4,40 @@
|
|||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func AddCombinedIndexToIssueUser(x *xorm.Engine) error {
|
||||
type OldIssueUser struct {
|
||||
IssueID int64
|
||||
UID int64
|
||||
Cnt int64
|
||||
}
|
||||
|
||||
var duplicatedIssueUsers []OldIssueUser
|
||||
if err := x.SQL("select * from (select issue_id, uid, count(1) as cnt from issue_user group by issue_id, uid) a where a.cnt > 1").
|
||||
Find(&duplicatedIssueUsers); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, issueUser := range duplicatedIssueUsers {
|
||||
if x.Dialect().URI().DBType == schemas.MSSQL {
|
||||
if _, err := x.Exec(fmt.Sprintf("delete from issue_user where id in (SELECT top %d id FROM issue_user WHERE issue_id = ? and uid = ?)", issueUser.Cnt-1), issueUser.IssueID, issueUser.UID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var ids []int64
|
||||
if err := x.SQL("SELECT id FROM issue_user WHERE issue_id = ? and uid = ? limit ?", issueUser.IssueID, issueUser.UID, issueUser.Cnt-1).Find(&ids); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := x.Table("issue_user").In("id", ids).Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IssueUser struct {
|
||||
UID int64 `xorm:"INDEX unique(uid_to_issue)"` // User ID.
|
||||
IssueID int64 `xorm:"INDEX unique(uid_to_issue)"`
|
||||
|
|
|
@ -36,9 +36,9 @@ func expandHashReferencesToSha256(x *xorm.Engine) error {
|
|||
if setting.Database.Type.IsMSSQL() {
|
||||
// drop indexes that need to be re-created afterwards
|
||||
droppedIndexes := []string{
|
||||
"DROP INDEX commit_status.IDX_commit_status_context_hash",
|
||||
"DROP INDEX review_state.UQE_review_state_pull_commit_user",
|
||||
"DROP INDEX repo_archiver.UQE_repo_archiver_s",
|
||||
"DROP INDEX IF EXISTS [IDX_commit_status_context_hash] ON [commit_status]",
|
||||
"DROP INDEX IF EXISTS [UQE_review_state_pull_commit_user] ON [review_state]",
|
||||
"DROP INDEX IF EXISTS [UQE_repo_archiver_s] ON [repo_archiver]",
|
||||
}
|
||||
for _, s := range droppedIndexes {
|
||||
_, err := db.Exec(s)
|
||||
|
@ -53,7 +53,7 @@ func expandHashReferencesToSha256(x *xorm.Engine) error {
|
|||
if setting.Database.Type.IsMySQL() {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
|
||||
} else if setting.Database.Type.IsMSSQL() {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1]))
|
||||
} else {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1]))
|
||||
}
|
||||
|
|
|
@ -15,58 +15,74 @@ import (
|
|||
func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) {
|
||||
type Repository struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"`
|
||||
}
|
||||
|
||||
type CommitStatus struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ContextHash string `xorm:"char(40)"`
|
||||
type CommitStatus struct {
|
||||
ID int64
|
||||
ContextHash string
|
||||
}
|
||||
|
||||
type Comment struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
type RepoArchiver struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
Type int
|
||||
CommitID string
|
||||
}
|
||||
|
||||
type PullRequest struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
MergeBase string `xorm:"VARCHAR(40)"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
type ReviewState struct {
|
||||
ID int64
|
||||
CommitSHA string
|
||||
UserID int64
|
||||
PullID int64
|
||||
}
|
||||
|
||||
type Review struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitID string `xorm:"VARCHAR(40)"`
|
||||
type Comment struct {
|
||||
ID int64
|
||||
CommitSHA string
|
||||
}
|
||||
|
||||
type ReviewState struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
type PullRequest struct {
|
||||
ID int64
|
||||
CommitSHA string
|
||||
MergeBase string
|
||||
MergedCommitID string
|
||||
}
|
||||
|
||||
type RepoArchiver struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitID string `xorm:"VARCHAR(40)"`
|
||||
type Release struct {
|
||||
ID int64
|
||||
Sha1 string
|
||||
}
|
||||
|
||||
type Release struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Sha1 string `xorm:"VARCHAR(40)"`
|
||||
type RepoIndexerStatus struct {
|
||||
ID int64
|
||||
CommitSHA string
|
||||
}
|
||||
|
||||
type RepoIndexerStatus struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CommitSha string `xorm:"VARCHAR(40)"`
|
||||
type Review struct {
|
||||
ID int64
|
||||
CommitID string
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
return base.PrepareTestEnv(t, 0, new(Repository), new(CommitStatus), new(Comment), new(PullRequest), new(Review), new(ReviewState), new(RepoArchiver), new(Release), new(RepoIndexerStatus))
|
||||
return base.PrepareTestEnv(t, 0,
|
||||
new(Repository),
|
||||
new(CommitStatus),
|
||||
new(RepoArchiver),
|
||||
new(ReviewState),
|
||||
new(Review),
|
||||
new(Comment),
|
||||
new(PullRequest),
|
||||
new(Release),
|
||||
new(RepoIndexerStatus),
|
||||
)
|
||||
}
|
||||
|
||||
func Test_RepositoryFormat(t *testing.T) {
|
||||
x, deferable := PrepareOldRepository(t)
|
||||
defer deferable()
|
||||
|
||||
assert.NoError(t, AdjustDBForSha256(x))
|
||||
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ObjectFormatName string `xorg:"not null default('sha1')"`
|
||||
|
@ -79,12 +95,10 @@ func Test_RepositoryFormat(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 4, count)
|
||||
|
||||
assert.NoError(t, AdjustDBForSha256(x))
|
||||
|
||||
repo.ID = 20
|
||||
repo.ObjectFormatName = "sha256"
|
||||
_, err = x.Insert(repo)
|
||||
assert.NoError(t, err)
|
||||
id := repo.ID
|
||||
|
||||
count, err = x.Count(new(Repository))
|
||||
assert.NoError(t, err)
|
||||
|
@ -97,7 +111,7 @@ func Test_RepositoryFormat(t *testing.T) {
|
|||
assert.EqualValues(t, "sha1", repo.ObjectFormatName)
|
||||
|
||||
repo = new(Repository)
|
||||
ok, err = x.ID(20).Get(repo)
|
||||
ok, err = x.ID(id).Get(repo)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, true, ok)
|
||||
assert.EqualValues(t, "sha256", repo.ObjectFormatName)
|
||||
|
|
46
models/migrations/v1_22/v287.go
Normal file
46
models/migrations/v1_22/v287.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type BadgeUnique struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Slug string `xorm:"UNIQUE"`
|
||||
}
|
||||
|
||||
func (BadgeUnique) TableName() string {
|
||||
return "badge"
|
||||
}
|
||||
|
||||
func UseSlugInsteadOfIDForBadges(x *xorm.Engine) error {
|
||||
type Badge struct {
|
||||
Slug string
|
||||
}
|
||||
|
||||
err := x.Sync(new(Badge))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec("UPDATE `badge` SET `slug` = `id` Where `slug` IS NULL")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sess.Sync(new(BadgeUnique))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
26
models/migrations/v1_22/v288.go
Normal file
26
models/migrations/v1_22/v288.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type Blocking struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
BlockerID int64 `xorm:"UNIQUE(block)"`
|
||||
BlockeeID int64 `xorm:"UNIQUE(block)"`
|
||||
Note string
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
|
||||
func (*Blocking) TableName() string {
|
||||
return "user_blocking"
|
||||
}
|
||||
|
||||
func AddUserBlockingTable(x *xorm.Engine) error {
|
||||
return x.Sync(&Blocking{})
|
||||
}
|
18
models/migrations/v1_22/v289.go
Normal file
18
models/migrations/v1_22/v289.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func AddDefaultWikiBranch(x *xorm.Engine) error {
|
||||
type Repository struct {
|
||||
ID int64
|
||||
DefaultWikiBranch string
|
||||
}
|
||||
if err := x.Sync(&Repository{}); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := x.Exec("UPDATE `repository` SET default_wiki_branch = 'master' WHERE (default_wiki_branch IS NULL) OR (default_wiki_branch = '')")
|
||||
return err
|
||||
}
|
39
models/migrations/v1_22/v290.go
Normal file
39
models/migrations/v1_22/v290.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// HookTask represents a hook task.
|
||||
// exact copy of models/webhook/hooktask.go when this migration was created
|
||||
// - xorm:"-" fields deleted
|
||||
type HookTask struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
HookID int64 `xorm:"index"`
|
||||
UUID string `xorm:"unique"`
|
||||
PayloadContent string `xorm:"LONGTEXT"`
|
||||
EventType webhook_module.HookEventType
|
||||
IsDelivered bool
|
||||
Delivered timeutil.TimeStampNano
|
||||
|
||||
// History info.
|
||||
IsSucceed bool
|
||||
RequestContent string `xorm:"LONGTEXT"`
|
||||
ResponseContent string `xorm:"LONGTEXT"`
|
||||
|
||||
// Version number to allow for smooth version upgrades:
|
||||
// - Version 1: PayloadContent contains the JSON as send to the URL
|
||||
// - Version 2: PayloadContent contains the original event
|
||||
PayloadVersion int `xorm:"DEFAULT 1"`
|
||||
}
|
||||
|
||||
func AddPayloadVersionToHookTaskTable(x *xorm.Engine) error {
|
||||
// create missing column
|
||||
return x.Sync(new(HookTask))
|
||||
}
|
58
models/migrations/v1_22/v290_test.go
Normal file
58
models/migrations/v1_22/v290_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_AddPayloadVersionToHookTaskTable(t *testing.T) {
|
||||
type HookTaskMigrated HookTask
|
||||
|
||||
// HookTask represents a hook task, as of before the migration
|
||||
type HookTask struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
HookID int64 `xorm:"index"`
|
||||
UUID string `xorm:"unique"`
|
||||
PayloadContent string `xorm:"LONGTEXT"`
|
||||
EventType webhook_module.HookEventType
|
||||
IsDelivered bool
|
||||
Delivered timeutil.TimeStampNano
|
||||
|
||||
// History info.
|
||||
IsSucceed bool
|
||||
RequestContent string `xorm:"LONGTEXT"`
|
||||
ResponseContent string `xorm:"LONGTEXT"`
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(HookTask), new(HookTaskMigrated))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, AddPayloadVersionToHookTaskTable(x))
|
||||
|
||||
expected := []HookTaskMigrated{}
|
||||
assert.NoError(t, x.Table("hook_task_migrated").Asc("id").Find(&expected))
|
||||
assert.Len(t, expected, 2)
|
||||
|
||||
got := []HookTaskMigrated{}
|
||||
assert.NoError(t, x.Table("hook_task").Asc("id").Find(&got))
|
||||
|
||||
for i, expected := range expected {
|
||||
expected, got := expected, got[i]
|
||||
t.Run(strconv.FormatInt(expected.ID, 10), func(t *testing.T) {
|
||||
assert.Equal(t, expected.PayloadVersion, got.PayloadVersion)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -70,16 +70,26 @@ type PackageFileDescriptor struct {
|
|||
Properties PackagePropertyList
|
||||
}
|
||||
|
||||
// PackageWebLink returns the package web link
|
||||
// PackageWebLink returns the relative package web link
|
||||
func (pd *PackageDescriptor) PackageWebLink() string {
|
||||
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
|
||||
}
|
||||
|
||||
// FullWebLink returns the package version web link
|
||||
func (pd *PackageDescriptor) FullWebLink() string {
|
||||
// VersionWebLink returns the relative package version web link
|
||||
func (pd *PackageDescriptor) VersionWebLink() string {
|
||||
return fmt.Sprintf("%s/%s", pd.PackageWebLink(), url.PathEscape(pd.Version.LowerVersion))
|
||||
}
|
||||
|
||||
// PackageHTMLURL returns the absolute package HTML URL
|
||||
func (pd *PackageDescriptor) PackageHTMLURL() string {
|
||||
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HTMLURL(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
|
||||
}
|
||||
|
||||
// VersionHTMLURL returns the absolute package version HTML URL
|
||||
func (pd *PackageDescriptor) VersionHTMLURL() string {
|
||||
return fmt.Sprintf("%s/%s", pd.PackageHTMLURL(), url.PathEscape(pd.Version.LowerVersion))
|
||||
}
|
||||
|
||||
// CalculateBlobSize returns the total blobs size in bytes
|
||||
func (pd *PackageDescriptor) CalculateBlobSize() int64 {
|
||||
size := int64(0)
|
||||
|
|
|
@ -55,7 +55,7 @@ func CountPackages(ctx context.Context, opts *packages_model.PackageSearchOption
|
|||
|
||||
func toConds(opts *packages_model.PackageSearchOptions) builder.Cond {
|
||||
var cond builder.Cond = builder.Eq{
|
||||
"package.is_internal": opts.IsInternal.IsTrue(),
|
||||
"package.is_internal": opts.IsInternal.Value(),
|
||||
"package.owner_id": opts.OwnerID,
|
||||
"package.type": packages_model.TypeNuGet,
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -105,7 +106,7 @@ func getVersionByNameAndVersion(ctx context.Context, ownerID int64, packageType
|
|||
ExactMatch: true,
|
||||
Value: version,
|
||||
},
|
||||
IsInternal: util.OptionalBoolOf(isInternal),
|
||||
IsInternal: optional.Some(isInternal),
|
||||
Paginator: db.NewAbsoluteListOptions(0, 1),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -122,7 +123,7 @@ func GetVersionsByPackageType(ctx context.Context, ownerID int64, packageType Ty
|
|||
pvs, _, err := SearchVersions(ctx, &PackageSearchOptions{
|
||||
OwnerID: ownerID,
|
||||
Type: packageType,
|
||||
IsInternal: util.OptionalBoolFalse,
|
||||
IsInternal: optional.Some(false),
|
||||
})
|
||||
return pvs, err
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ func GetVersionsByPackageName(ctx context.Context, ownerID int64, packageType Ty
|
|||
ExactMatch: true,
|
||||
Value: name,
|
||||
},
|
||||
IsInternal: util.OptionalBoolFalse,
|
||||
IsInternal: optional.Some(false),
|
||||
})
|
||||
return pvs, err
|
||||
}
|
||||
|
@ -182,18 +183,18 @@ type PackageSearchOptions struct {
|
|||
Name SearchValue // only results with the specific name are found
|
||||
Version SearchValue // only results with the specific version are found
|
||||
Properties map[string]string // only results are found which contain all listed version properties with the specific value
|
||||
IsInternal util.OptionalBool
|
||||
IsInternal optional.Option[bool]
|
||||
HasFileWithName string // only results are found which are associated with a file with the specific name
|
||||
HasFiles util.OptionalBool // only results are found which have associated files
|
||||
HasFiles optional.Option[bool] // only results are found which have associated files
|
||||
Sort VersionSort
|
||||
db.Paginator
|
||||
}
|
||||
|
||||
func (opts *PackageSearchOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if !opts.IsInternal.IsNone() {
|
||||
if opts.IsInternal.Has() {
|
||||
cond = builder.Eq{
|
||||
"package_version.is_internal": opts.IsInternal.IsTrue(),
|
||||
"package_version.is_internal": opts.IsInternal.Value(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,10 +251,10 @@ func (opts *PackageSearchOptions) ToConds() builder.Cond {
|
|||
cond = cond.And(builder.Exists(builder.Select("package_file.id").From("package_file").Where(fileCond)))
|
||||
}
|
||||
|
||||
if !opts.HasFiles.IsNone() {
|
||||
if opts.HasFiles.Has() {
|
||||
filesCond := builder.Exists(builder.Select("package_file.id").From("package_file").Where(builder.Expr("package_file.version_id = package_version.id")))
|
||||
|
||||
if opts.HasFiles.IsFalse() {
|
||||
if !opts.HasFiles.Value() {
|
||||
filesCond = builder.Not{filesCond}
|
||||
}
|
||||
|
||||
|
@ -307,8 +308,8 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
|
|||
And(builder.Expr("pv2.id IS NULL"))
|
||||
|
||||
joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
|
||||
if !opts.IsInternal.IsNone() {
|
||||
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.IsTrue()})
|
||||
if opts.IsInternal.Has() {
|
||||
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
|
||||
}
|
||||
|
||||
sess := db.GetEngine(ctx).
|
||||
|
|
|
@ -232,7 +232,7 @@ func UpdateBoard(ctx context.Context, board *Board) error {
|
|||
func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
|
||||
boards := make([]*Board, 0, 5)
|
||||
|
||||
if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("Sorting").Find(&boards); err != nil {
|
||||
if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,13 @@ package project
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -100,7 +102,7 @@ type Project struct {
|
|||
CardType CardType
|
||||
Type Type
|
||||
|
||||
RenderedContent string `xorm:"-"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
|
@ -195,7 +197,7 @@ type SearchOptions struct {
|
|||
db.ListOptions
|
||||
OwnerID int64
|
||||
RepoID int64
|
||||
IsClosed util.OptionalBool
|
||||
IsClosed optional.Option[bool]
|
||||
OrderBy db.SearchOrderBy
|
||||
Type Type
|
||||
Title string
|
||||
|
@ -206,11 +208,8 @@ func (opts SearchOptions) ToConds() builder.Cond {
|
|||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
switch opts.IsClosed {
|
||||
case util.OptionalBoolTrue:
|
||||
cond = cond.And(builder.Eq{"is_closed": true})
|
||||
case util.OptionalBoolFalse:
|
||||
cond = cond.And(builder.Eq{"is_closed": false})
|
||||
if opts.IsClosed.Has() {
|
||||
cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()})
|
||||
}
|
||||
|
||||
if opts.Type > 0 {
|
||||
|
|
|
@ -7,6 +7,7 @@ package repo
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -79,7 +81,7 @@ type Release struct {
|
|||
NumCommits int64
|
||||
NumCommitsBehind int64 `xorm:"-"`
|
||||
Note string `xorm:"TEXT"`
|
||||
RenderedNote string `xorm:"-"`
|
||||
RenderedNote template.HTML `xorm:"-"`
|
||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
|
||||
|
@ -228,10 +230,10 @@ type FindReleasesOptions struct {
|
|||
RepoID int64
|
||||
IncludeDrafts bool
|
||||
IncludeTags bool
|
||||
IsPreRelease util.OptionalBool
|
||||
IsDraft util.OptionalBool
|
||||
IsPreRelease optional.Option[bool]
|
||||
IsDraft optional.Option[bool]
|
||||
TagNames []string
|
||||
HasSha1 util.OptionalBool // useful to find draft releases which are created with existing tags
|
||||
HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags
|
||||
}
|
||||
|
||||
func (opts FindReleasesOptions) ToConds() builder.Cond {
|
||||
|
@ -246,14 +248,14 @@ func (opts FindReleasesOptions) ToConds() builder.Cond {
|
|||
if len(opts.TagNames) > 0 {
|
||||
cond = cond.And(builder.In("tag_name", opts.TagNames))
|
||||
}
|
||||
if !opts.IsPreRelease.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.IsTrue()})
|
||||
if opts.IsPreRelease.Has() {
|
||||
cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.Value()})
|
||||
}
|
||||
if !opts.IsDraft.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()})
|
||||
if opts.IsDraft.Has() {
|
||||
cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.Value()})
|
||||
}
|
||||
if !opts.HasSha1.IsNone() {
|
||||
if opts.HasSha1.IsTrue() {
|
||||
if opts.HasSha1.Has() {
|
||||
if opts.HasSha1.Value() {
|
||||
cond = cond.And(builder.Neq{"sha1": ""})
|
||||
} else {
|
||||
cond = cond.And(builder.Eq{"sha1": ""})
|
||||
|
@ -275,7 +277,7 @@ func GetTagNamesByRepoID(ctx context.Context, repoID int64) ([]string, error) {
|
|||
ListOptions: listOptions,
|
||||
IncludeDrafts: true,
|
||||
IncludeTags: true,
|
||||
HasSha1: util.OptionalBoolTrue,
|
||||
HasSha1: optional.Some(true),
|
||||
RepoID: repoID,
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
@ -873,7 +874,7 @@ func (repo *Repository) TemplateRepo(ctx context.Context) *Repository {
|
|||
|
||||
type CountRepositoryOptions struct {
|
||||
OwnerID int64
|
||||
Private util.OptionalBool
|
||||
Private optional.Option[bool]
|
||||
}
|
||||
|
||||
// CountRepositories returns number of repositories.
|
||||
|
@ -885,8 +886,8 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64,
|
|||
if opts.OwnerID > 0 {
|
||||
sess.And("owner_id = ?", opts.OwnerID)
|
||||
}
|
||||
if !opts.Private.IsNone() {
|
||||
sess.And("is_private=?", opts.Private.IsTrue())
|
||||
if opts.Private.Has() {
|
||||
sess.And("is_private=?", opts.Private.Value())
|
||||
}
|
||||
|
||||
count, err := sess.Count(new(Repository))
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -125,11 +126,11 @@ type SearchRepoOptions struct {
|
|||
// None -> include public and private
|
||||
// True -> include just private
|
||||
// False -> include just public
|
||||
IsPrivate util.OptionalBool
|
||||
IsPrivate optional.Option[bool]
|
||||
// None -> include collaborative AND non-collaborative
|
||||
// True -> include just collaborative
|
||||
// False -> include just non-collaborative
|
||||
Collaborate util.OptionalBool
|
||||
Collaborate optional.Option[bool]
|
||||
// What type of unit the user can be collaborative in,
|
||||
// it is ignored if Collaborate is False.
|
||||
// TypeInvalid means any unit type.
|
||||
|
@ -137,19 +138,19 @@ type SearchRepoOptions struct {
|
|||
// None -> include forks AND non-forks
|
||||
// True -> include just forks
|
||||
// False -> include just non-forks
|
||||
Fork util.OptionalBool
|
||||
Fork optional.Option[bool]
|
||||
// None -> include templates AND non-templates
|
||||
// True -> include just templates
|
||||
// False -> include just non-templates
|
||||
Template util.OptionalBool
|
||||
Template optional.Option[bool]
|
||||
// None -> include mirrors AND non-mirrors
|
||||
// True -> include just mirrors
|
||||
// False -> include just non-mirrors
|
||||
Mirror util.OptionalBool
|
||||
Mirror optional.Option[bool]
|
||||
// None -> include archived AND non-archived
|
||||
// True -> include just archived
|
||||
// False -> include just non-archived
|
||||
Archived util.OptionalBool
|
||||
Archived optional.Option[bool]
|
||||
// only search topic name
|
||||
TopicOnly bool
|
||||
// only search repositories with specified primary language
|
||||
|
@ -159,7 +160,7 @@ type SearchRepoOptions struct {
|
|||
// None -> include has milestones AND has no milestone
|
||||
// True -> include just has milestones
|
||||
// False -> include just has no milestone
|
||||
HasMilestones util.OptionalBool
|
||||
HasMilestones optional.Option[bool]
|
||||
// LowerNames represents valid lower names to restrict to
|
||||
LowerNames []string
|
||||
// When specified true, apply some filters over the conditions:
|
||||
|
@ -359,12 +360,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
|||
)))
|
||||
}
|
||||
|
||||
if opts.IsPrivate != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()})
|
||||
if opts.IsPrivate.Has() {
|
||||
cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.Value()})
|
||||
}
|
||||
|
||||
if opts.Template != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue})
|
||||
if opts.Template.Has() {
|
||||
cond = cond.And(builder.Eq{"is_template": opts.Template.Value()})
|
||||
}
|
||||
|
||||
// Restrict to starred repositories
|
||||
|
@ -380,11 +381,11 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
|||
// Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
|
||||
if opts.OwnerID > 0 {
|
||||
accessCond := builder.NewCond()
|
||||
if opts.Collaborate != util.OptionalBoolTrue {
|
||||
if !opts.Collaborate.Value() {
|
||||
accessCond = builder.Eq{"owner_id": opts.OwnerID}
|
||||
}
|
||||
|
||||
if opts.Collaborate != util.OptionalBoolFalse {
|
||||
if opts.Collaborate.ValueOrDefault(true) {
|
||||
// A Collaboration is:
|
||||
|
||||
collaborateCond := builder.NewCond()
|
||||
|
@ -472,32 +473,33 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
|||
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
|
||||
}
|
||||
|
||||
if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant {
|
||||
if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone {
|
||||
if opts.Fork.Has() || opts.OnlyShowRelevant {
|
||||
if opts.OnlyShowRelevant && !opts.Fork.Has() {
|
||||
cond = cond.And(builder.Eq{"is_fork": false})
|
||||
} else {
|
||||
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
|
||||
cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Mirror != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
|
||||
if opts.Mirror.Has() {
|
||||
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror.Value()})
|
||||
}
|
||||
|
||||
if opts.Actor != nil && opts.Actor.IsRestricted {
|
||||
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
|
||||
}
|
||||
|
||||
if opts.Archived != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
|
||||
if opts.Archived.Has() {
|
||||
cond = cond.And(builder.Eq{"is_archived": opts.Archived.Value()})
|
||||
}
|
||||
|
||||
switch opts.HasMilestones {
|
||||
case util.OptionalBoolTrue:
|
||||
if opts.HasMilestones.Has() {
|
||||
if opts.HasMilestones.Value() {
|
||||
cond = cond.And(builder.Gt{"num_milestones": 0})
|
||||
case util.OptionalBoolFalse:
|
||||
} else {
|
||||
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
|
||||
}
|
||||
}
|
||||
|
||||
if opts.OnlyShowRelevant {
|
||||
// Only show a repo that has at least a topic, an icon, or a description
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -27,62 +27,62 @@ func getTestCases() []struct {
|
|||
}{
|
||||
{
|
||||
name: "PublicRepositoriesByName",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)},
|
||||
count: 7,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesByName",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "PublicRepositoriesOfUser",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)},
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
name: "PublicRepositoriesOfUser2",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)},
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: "PublicRepositoriesOfOrg3",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)},
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesOfUser",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 4,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesOfUser2",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesOfOrg3",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 4,
|
||||
},
|
||||
{
|
||||
|
@ -117,32 +117,32 @@ func getTestCases() []struct {
|
|||
},
|
||||
{
|
||||
name: "PublicRepositoriesOfOrganization",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)},
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
name: "PublicAndPrivateRepositoriesOfOrganization",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)},
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
name: "AllPublic/PublicRepositoriesByName",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)},
|
||||
count: 7,
|
||||
},
|
||||
{
|
||||
name: "AllPublic/PublicAndPrivateRepositoriesByName",
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)},
|
||||
count: 14,
|
||||
},
|
||||
{
|
||||
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)},
|
||||
count: 34,
|
||||
},
|
||||
{
|
||||
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)},
|
||||
count: 39,
|
||||
},
|
||||
{
|
||||
|
@ -157,12 +157,12 @@ func getTestCases() []struct {
|
|||
},
|
||||
{
|
||||
name: "AllPublic/PublicRepositoriesOfOrganization",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)},
|
||||
count: 34,
|
||||
},
|
||||
{
|
||||
name: "AllTemplates",
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
|
||||
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)},
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
|
@ -190,7 +190,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
PageSize: 10,
|
||||
},
|
||||
Keyword: "repo_12",
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
@ -205,7 +205,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
PageSize: 10,
|
||||
},
|
||||
Keyword: "test_repo",
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
@ -220,7 +220,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
},
|
||||
Keyword: "repo_13",
|
||||
Private: true,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
@ -236,7 +236,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
},
|
||||
Keyword: "test_repo",
|
||||
Private: true,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
@ -257,7 +257,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
PageSize: 10,
|
||||
},
|
||||
Keyword: "description_14",
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
IncludeDescription: true,
|
||||
})
|
||||
|
||||
|
@ -274,7 +274,7 @@ func TestSearchRepository(t *testing.T) {
|
|||
PageSize: 10,
|
||||
},
|
||||
Keyword: "description_14",
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
Collaborate: optional.Some(false),
|
||||
IncludeDescription: false,
|
||||
})
|
||||
|
||||
|
@ -327,30 +327,25 @@ func TestSearchRepository(t *testing.T) {
|
|||
assert.False(t, repo.IsPrivate)
|
||||
}
|
||||
|
||||
if testCase.opts.Fork == util.OptionalBoolTrue && testCase.opts.Mirror == util.OptionalBoolTrue {
|
||||
assert.True(t, repo.IsFork || repo.IsMirror)
|
||||
if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() {
|
||||
assert.True(t, repo.IsFork && repo.IsMirror)
|
||||
} else {
|
||||
switch testCase.opts.Fork {
|
||||
case util.OptionalBoolFalse:
|
||||
assert.False(t, repo.IsFork)
|
||||
case util.OptionalBoolTrue:
|
||||
assert.True(t, repo.IsFork)
|
||||
if testCase.opts.Fork.Has() {
|
||||
assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork)
|
||||
}
|
||||
|
||||
switch testCase.opts.Mirror {
|
||||
case util.OptionalBoolFalse:
|
||||
assert.False(t, repo.IsMirror)
|
||||
case util.OptionalBoolTrue:
|
||||
assert.True(t, repo.IsMirror)
|
||||
if testCase.opts.Mirror.Has() {
|
||||
assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror)
|
||||
}
|
||||
}
|
||||
|
||||
if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
|
||||
switch testCase.opts.Collaborate {
|
||||
case util.OptionalBoolFalse:
|
||||
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
|
||||
case util.OptionalBoolTrue:
|
||||
if testCase.opts.Collaborate.Has() {
|
||||
if testCase.opts.Collaborate.Value() {
|
||||
assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
|
||||
} else {
|
||||
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,17 @@ import (
|
|||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
countRepospts = repo_model.CountRepositoryOptions{OwnerID: 10}
|
||||
countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse}
|
||||
countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue}
|
||||
countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: optional.Some(false)}
|
||||
countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: optional.Some(true)}
|
||||
)
|
||||
|
||||
func TestGetRepositoryCount(t *testing.T) {
|
||||
|
|
|
@ -9,7 +9,10 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
actions_module "code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
secret_module "code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
@ -112,3 +115,39 @@ func UpdateSecret(ctx context.Context, secretID int64, data string) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) {
|
||||
secrets := map[string]string{}
|
||||
|
||||
secrets["GITHUB_TOKEN"] = task.Token
|
||||
secrets["GITEA_TOKEN"] = task.Token
|
||||
|
||||
if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
||||
// ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated.
|
||||
// for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch
|
||||
// see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
return secrets, nil
|
||||
}
|
||||
|
||||
ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID})
|
||||
if err != nil {
|
||||
log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err)
|
||||
return nil, err
|
||||
}
|
||||
repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID})
|
||||
if err != nil {
|
||||
log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, secret := range append(ownerSecrets, repoSecrets...) {
|
||||
v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data)
|
||||
if err != nil {
|
||||
log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err)
|
||||
return nil, err
|
||||
}
|
||||
secrets[secret.Name] = v
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM
|
|||
path := NormalizedFullPath(r.URL)
|
||||
log.Info("Mock HTTP Server: got request for path %s", r.URL.Path)
|
||||
// TODO check request method (support POST?)
|
||||
fixturePath := fmt.Sprintf("%s/%s", testDataDir, strings.NewReplacer("/", "_", "?", "!").Replace(path))
|
||||
fixturePath := fmt.Sprintf("%s/%s_%s", testDataDir, r.Method, url.PathEscape(path))
|
||||
if liveMode {
|
||||
liveURL := fmt.Sprintf("%s%s", liveServerBaseURL, path)
|
||||
|
||||
|
@ -51,6 +51,7 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM
|
|||
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
assert.NoError(t, err, "HTTP request to %s failed: %s", liveURL)
|
||||
assert.Less(t, response.StatusCode, 400, "unexpected status code for %s", liveURL)
|
||||
|
||||
fixture, err := os.Create(fixturePath)
|
||||
assert.NoError(t, err, "failed to open the fixture file %s for writing", fixturePath)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
|
@ -156,37 +157,18 @@ func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error {
|
|||
|
||||
var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
|
||||
// ValidateEmail check if email is a allowed address
|
||||
// ValidateEmail check if email is a valid & allowed address
|
||||
func ValidateEmail(email string) error {
|
||||
if len(email) == 0 {
|
||||
return ErrEmailInvalid{email}
|
||||
if err := validateEmailBasic(email); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateEmailDomain(email)
|
||||
}
|
||||
|
||||
if !emailRegexp.MatchString(email) {
|
||||
return ErrEmailCharIsNotSupported{email}
|
||||
}
|
||||
|
||||
if email[0] == '-' {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
if _, err := mail.ParseAddress(email); err != nil {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
// if there is no allow list, then check email against block list
|
||||
if len(setting.Service.EmailDomainAllowList) == 0 &&
|
||||
validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
// if there is an allow list, then check email against allow list
|
||||
if len(setting.Service.EmailDomainAllowList) > 0 &&
|
||||
!validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
return nil
|
||||
// ValidateEmailForAdmin check if email is a valid address when admins manually add or edit users
|
||||
func ValidateEmailForAdmin(email string) error {
|
||||
return validateEmailBasic(email)
|
||||
// In this case we do not need to check the email domain
|
||||
}
|
||||
|
||||
func GetEmailAddressByEmail(ctx context.Context, email string) (*EmailAddress, error) {
|
||||
|
@ -425,8 +407,8 @@ type SearchEmailOptions struct {
|
|||
db.ListOptions
|
||||
Keyword string
|
||||
SortType SearchEmailOrderBy
|
||||
IsPrimary util.OptionalBool
|
||||
IsActivated util.OptionalBool
|
||||
IsPrimary optional.Option[bool]
|
||||
IsActivated optional.Option[bool]
|
||||
}
|
||||
|
||||
// SearchEmailResult is an e-mail address found in the user or email_address table
|
||||
|
@ -453,18 +435,12 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
|
|||
))
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.IsPrimary.IsTrue():
|
||||
cond = cond.And(builder.Eq{"email_address.is_primary": true})
|
||||
case opts.IsPrimary.IsFalse():
|
||||
cond = cond.And(builder.Eq{"email_address.is_primary": false})
|
||||
if opts.IsPrimary.Has() {
|
||||
cond = cond.And(builder.Eq{"email_address.is_primary": opts.IsPrimary.Value()})
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.IsActivated.IsTrue():
|
||||
cond = cond.And(builder.Eq{"email_address.is_activated": true})
|
||||
case opts.IsActivated.IsFalse():
|
||||
cond = cond.And(builder.Eq{"email_address.is_activated": false})
|
||||
if opts.IsActivated.Has() {
|
||||
cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()})
|
||||
}
|
||||
|
||||
count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid").
|
||||
|
@ -548,3 +524,41 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate
|
|||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
// validateEmailBasic checks whether the email complies with the rules
|
||||
func validateEmailBasic(email string) error {
|
||||
if len(email) == 0 {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
if !emailRegexp.MatchString(email) {
|
||||
return ErrEmailCharIsNotSupported{email}
|
||||
}
|
||||
|
||||
if email[0] == '-' {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
if _, err := mail.ParseAddress(email); err != nil {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateEmailDomain checks whether the email domain is allowed or blocked
|
||||
func validateEmailDomain(email string) error {
|
||||
// if there is no allow list, then check email against block list
|
||||
if len(setting.Service.EmailDomainAllowList) == 0 &&
|
||||
validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
// if there is an allow list, then check email against allow list
|
||||
if len(setting.Service.EmailDomainAllowList) > 0 &&
|
||||
!validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -138,14 +138,14 @@ func TestListEmails(t *testing.T) {
|
|||
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 27 }))
|
||||
|
||||
// Must find only primary addresses (i.e. from the `user` table)
|
||||
opts = &user_model.SearchEmailOptions{IsPrimary: util.OptionalBoolTrue}
|
||||
opts = &user_model.SearchEmailOptions{IsPrimary: optional.Some(true)}
|
||||
emails, _, err = user_model.SearchEmails(db.DefaultContext, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsPrimary }))
|
||||
assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsPrimary }))
|
||||
|
||||
// Must find only inactive addresses (i.e. not validated)
|
||||
opts = &user_model.SearchEmailOptions{IsActivated: util.OptionalBoolFalse}
|
||||
opts = &user_model.SearchEmailOptions{IsActivated: optional.Some(false)}
|
||||
emails, _, err = user_model.SearchEmails(db.DefaultContext, opts)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsActivated }))
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
@ -30,11 +31,13 @@ type SearchUserOptions struct {
|
|||
Actor *User // The user doing the search
|
||||
SearchByEmail bool // Search by email as well as username/full name
|
||||
|
||||
IsActive util.OptionalBool
|
||||
IsAdmin util.OptionalBool
|
||||
IsRestricted util.OptionalBool
|
||||
IsTwoFactorEnabled util.OptionalBool
|
||||
IsProhibitLogin util.OptionalBool
|
||||
SupportedSortOrders container.Set[string] // if not nil, only allow to use the sort orders in this set
|
||||
|
||||
IsActive optional.Option[bool]
|
||||
IsAdmin optional.Option[bool]
|
||||
IsRestricted optional.Option[bool]
|
||||
IsTwoFactorEnabled optional.Option[bool]
|
||||
IsProhibitLogin optional.Option[bool]
|
||||
IncludeReserved bool
|
||||
|
||||
ExtraParamStrings map[string]string
|
||||
|
@ -86,24 +89,24 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
|||
cond = cond.And(builder.Eq{"login_name": opts.LoginName})
|
||||
}
|
||||
|
||||
if !opts.IsActive.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()})
|
||||
if opts.IsActive.Has() {
|
||||
cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
|
||||
}
|
||||
|
||||
if !opts.IsAdmin.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()})
|
||||
if opts.IsAdmin.Has() {
|
||||
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
|
||||
}
|
||||
|
||||
if !opts.IsRestricted.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.IsTrue()})
|
||||
if opts.IsRestricted.Has() {
|
||||
cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.Value()})
|
||||
}
|
||||
|
||||
if !opts.IsProhibitLogin.IsNone() {
|
||||
cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.IsTrue()})
|
||||
if opts.IsProhibitLogin.Has() {
|
||||
cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()})
|
||||
}
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
if opts.IsTwoFactorEnabled.IsNone() {
|
||||
if !opts.IsTwoFactorEnabled.Has() {
|
||||
return e.Where(cond)
|
||||
}
|
||||
|
||||
|
@ -111,7 +114,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
|||
// While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed.
|
||||
// There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now):
|
||||
// (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch)
|
||||
if opts.IsTwoFactorEnabled.IsTrue() {
|
||||
if opts.IsTwoFactorEnabled.Value() {
|
||||
cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL"))
|
||||
} else {
|
||||
cond = cond.And(builder.Expr("two_factor.uid IS NULL"))
|
||||
|
@ -128,7 +131,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
|
|||
defer sessCount.Close()
|
||||
count, err := sessCount.Count(new(User))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
return nil, 0, fmt.Errorf("count: %w", err)
|
||||
}
|
||||
|
||||
if len(opts.OrderBy) == 0 {
|
||||
|
|
|
@ -598,6 +598,16 @@ type CreateUserOverwriteOptions struct {
|
|||
|
||||
// CreateUser creates record of a new user.
|
||||
func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||
return createUser(ctx, u, false, overwriteDefault...)
|
||||
}
|
||||
|
||||
// AdminCreateUser is used by admins to manually create users
|
||||
func AdminCreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||
return createUser(ctx, u, true, overwriteDefault...)
|
||||
}
|
||||
|
||||
// createUser creates record of a new user.
|
||||
func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||
if err = IsUsableUsername(u.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -651,9 +661,15 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve
|
|||
return err
|
||||
}
|
||||
|
||||
if createdByAdmin {
|
||||
if err := ValidateEmailForAdmin(u.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := ValidateEmail(u.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
|
@ -727,7 +743,7 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve
|
|||
|
||||
// IsLastAdminUser check whether user is the last admin
|
||||
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
||||
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
|
||||
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -736,7 +752,7 @@ func IsLastAdminUser(ctx context.Context, user *User) bool {
|
|||
// CountUserFilter represent optional filters for CountUsers
|
||||
type CountUserFilter struct {
|
||||
LastLoginSince *int64
|
||||
IsAdmin util.OptionalBool
|
||||
IsAdmin optional.Option[bool]
|
||||
}
|
||||
|
||||
// CountUsers returns number of users.
|
||||
|
@ -754,8 +770,8 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
|||
cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince})
|
||||
}
|
||||
|
||||
if !opts.IsAdmin.IsNone() {
|
||||
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()})
|
||||
if opts.IsAdmin.Has() {
|
||||
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@ import (
|
|||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -103,29 +103,29 @@ func TestSearchUsers(t *testing.T) {
|
|||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
|
||||
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse},
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
|
||||
[]int64{9})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||
|
||||
// order by name asc default
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
|
||||
[]int64{1})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
|
||||
[]int64{29})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
|
||||
[]int64{37})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: util.OptionalBoolTrue},
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
||||
[]int64{24})
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ package webhook
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
|
@ -31,6 +31,7 @@ type HookRequest struct {
|
|||
URL string `json:"url"`
|
||||
HTTPMethod string `json:"http_method"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// HookResponse represents hook task response information.
|
||||
|
@ -45,8 +46,12 @@ type HookTask struct {
|
|||
ID int64 `xorm:"pk autoincr"`
|
||||
HookID int64 `xorm:"index"`
|
||||
UUID string `xorm:"unique"`
|
||||
api.Payloader `xorm:"-"`
|
||||
PayloadContent string `xorm:"LONGTEXT"`
|
||||
// PayloadVersion number to allow for smooth version upgrades:
|
||||
// - PayloadVersion 1: PayloadContent contains the JSON as sent to the URL
|
||||
// - PayloadVersion 2: PayloadContent contains the original event
|
||||
PayloadVersion int `xorm:"DEFAULT 1"`
|
||||
|
||||
EventType webhook_module.HookEventType
|
||||
IsDelivered bool
|
||||
Delivered timeutil.TimeStampNano
|
||||
|
@ -115,16 +120,12 @@ func HookTasks(ctx context.Context, hookID int64, page int) ([]*HookTask, error)
|
|||
// it handles conversion from Payload to PayloadContent.
|
||||
func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
|
||||
t.UUID = gouuid.New().String()
|
||||
if t.Payloader != nil {
|
||||
data, err := t.Payloader.JSONPayload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.PayloadContent = string(data)
|
||||
}
|
||||
if t.Delivered == 0 {
|
||||
t.Delivered = timeutil.TimeStampNanoNow()
|
||||
}
|
||||
if t.PayloadVersion == 0 {
|
||||
return nil, errors.New("missing HookTask.PayloadVersion")
|
||||
}
|
||||
return t, db.Insert(ctx, t)
|
||||
}
|
||||
|
||||
|
@ -165,6 +166,7 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask,
|
|||
HookID: task.HookID,
|
||||
PayloadContent: task.PayloadContent,
|
||||
EventType: task.EventType,
|
||||
PayloadVersion: task.PayloadVersion,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
@ -433,7 +434,7 @@ type ListWebhookOptions struct {
|
|||
db.ListOptions
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
IsActive util.OptionalBool
|
||||
IsActive optional.Option[bool]
|
||||
}
|
||||
|
||||
func (opts ListWebhookOptions) ToConds() builder.Cond {
|
||||
|
@ -444,8 +445,8 @@ func (opts ListWebhookOptions) ToConds() builder.Cond {
|
|||
if opts.OwnerID != 0 {
|
||||
cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID})
|
||||
}
|
||||
if !opts.IsActive.IsNone() {
|
||||
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()})
|
||||
if opts.IsActive.Has() {
|
||||
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.Value()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
)
|
||||
|
||||
// GetDefaultWebhooks returns all admin-default webhooks.
|
||||
|
@ -34,15 +34,15 @@ func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error)
|
|||
}
|
||||
|
||||
// GetSystemWebhooks returns all admin system webhooks.
|
||||
func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webhook, error) {
|
||||
func GetSystemWebhooks(ctx context.Context, isActive optional.Option[bool]) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
if isActive.IsNone() {
|
||||
if !isActive.Has() {
|
||||
return webhooks, db.GetEngine(ctx).
|
||||
Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true).
|
||||
Find(&webhooks)
|
||||
}
|
||||
return webhooks, db.GetEngine(ctx).
|
||||
Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()).
|
||||
Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.Value()).
|
||||
Find(&webhooks)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,8 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -35,8 +34,10 @@ func TestWebhook_History(t *testing.T) {
|
|||
webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1})
|
||||
tasks, err := webhook.History(db.DefaultContext, 0)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, tasks, 1) {
|
||||
assert.Equal(t, int64(1), tasks[0].ID)
|
||||
if assert.Len(t, tasks, 3) {
|
||||
assert.Equal(t, int64(3), tasks[0].ID)
|
||||
assert.Equal(t, int64(2), tasks[1].ID)
|
||||
assert.Equal(t, int64(1), tasks[2].ID)
|
||||
}
|
||||
|
||||
webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2})
|
||||
|
@ -123,7 +124,7 @@ func TestGetWebhookByOwnerID(t *testing.T) {
|
|||
|
||||
func TestGetActiveWebhooksByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue})
|
||||
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hooks, 1) {
|
||||
assert.Equal(t, int64(1), hooks[0].ID)
|
||||
|
@ -143,7 +144,7 @@ func TestGetWebhooksByRepoID(t *testing.T) {
|
|||
|
||||
func TestGetActiveWebhooksByOwnerID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue})
|
||||
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hooks, 1) {
|
||||
assert.Equal(t, int64(3), hooks[0].ID)
|
||||
|
@ -197,8 +198,10 @@ func TestHookTasks(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTasks, err := HookTasks(db.DefaultContext, 1, 1)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, hookTasks, 1) {
|
||||
assert.Equal(t, int64(1), hookTasks[0].ID)
|
||||
if assert.Len(t, hookTasks, 3) {
|
||||
assert.Equal(t, int64(3), hookTasks[0].ID)
|
||||
assert.Equal(t, int64(2), hookTasks[1].ID)
|
||||
assert.Equal(t, int64(1), hookTasks[2].ID)
|
||||
}
|
||||
|
||||
hookTasks, err = HookTasks(db.DefaultContext, unittest.NonexistentID, 1)
|
||||
|
@ -210,7 +213,7 @@ func TestCreateHookTask(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 3,
|
||||
Payloader: &api.PushPayload{},
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -233,9 +236,9 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 3,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: true,
|
||||
Delivered: timeutil.TimeStampNanoNow(),
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -250,8 +253,8 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 4,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: false,
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -266,9 +269,9 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 4,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: true,
|
||||
Delivered: timeutil.TimeStampNanoNow(),
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -283,9 +286,9 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 3,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: true,
|
||||
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()),
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -300,8 +303,8 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 4,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: false,
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
@ -316,9 +319,9 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hookTask := &HookTask{
|
||||
HookID: 4,
|
||||
Payloader: &api.PushPayload{},
|
||||
IsDelivered: true,
|
||||
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()),
|
||||
PayloadVersion: 2,
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, hookTask)
|
||||
_, err := CreateHookTask(db.DefaultContext, hookTask)
|
||||
|
|
|
@ -35,6 +35,9 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
|
|||
} else if task.Status.IsDone() {
|
||||
preStep.Stopped = task.Stopped
|
||||
preStep.Status = actions_model.StatusFailure
|
||||
if task.Status.IsSkipped() {
|
||||
preStep.Status = actions_model.StatusSkipped
|
||||
}
|
||||
}
|
||||
logIndex += preStep.LogLength
|
||||
|
||||
|
|
|
@ -406,6 +406,9 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
|
|||
// all acts conditions should be satisfied
|
||||
for cond, vals := range acts {
|
||||
switch cond {
|
||||
case "types":
|
||||
// types have been checked
|
||||
continue
|
||||
case "branches":
|
||||
refName := git.RefName(prPayload.PullRequest.Base.Ref)
|
||||
patterns, err := workflowpattern.CompilePatterns(vals...)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -344,6 +345,17 @@ func (c *Command) Run(opts *RunOpts) error {
|
|||
log.Debug("slow git.Command.Run: %s (%s)", c, elapsed)
|
||||
}
|
||||
|
||||
// We need to check if the context is canceled by the program on Windows.
|
||||
// This is because Windows does not have signal checking when terminating the process.
|
||||
// It always returns exit code 1, unlike Linux, which has many exit codes for signals.
|
||||
if runtime.GOOS == "windows" &&
|
||||
err != nil &&
|
||||
err.Error() == "" &&
|
||||
cmd.ProcessState.ExitCode() == 1 &&
|
||||
ctx.Err() == context.Canceled {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
if err != nil && ctx.Err() != context.DeadlineExceeded {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -55,15 +55,8 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// SetDefaultBranch sets default branch of repository.
|
||||
func (repo *Repository) SetDefaultBranch(name string) error {
|
||||
_, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDefaultBranch gets default branch of repository.
|
||||
func (repo *Repository) GetDefaultBranch() (string, error) {
|
||||
stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path})
|
||||
func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) {
|
||||
stdout, _, err := NewCommand(ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -30,3 +30,20 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str
|
|||
|
||||
return gitRepo.GetBranchCommitID(branch)
|
||||
}
|
||||
|
||||
// SetDefaultBranch sets default branch of repository.
|
||||
func SetDefaultBranch(ctx context.Context, repo Repository, name string) error {
|
||||
_, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD").
|
||||
AddDynamicArguments(git.BranchPrefix + name).
|
||||
RunStdString(&git.RunOpts{Dir: repoPath(repo)})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDefaultBranch gets default branch of repository.
|
||||
func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
return git.GetDefaultBranch(ctx, repoPath(repo))
|
||||
}
|
||||
|
||||
func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
return git.GetDefaultBranch(ctx, wikiPath(repo))
|
||||
}
|
||||
|
|
|
@ -59,7 +59,15 @@ func (g *Manager) start() {
|
|||
go func() {
|
||||
defer close(startupDone)
|
||||
// Wait till we're done getting all the listeners and then close the unused ones
|
||||
func() {
|
||||
// FIXME: there is a fundamental design problem of the "manager" and the "wait group".
|
||||
// If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned
|
||||
// There is no clear solution besides a complete rewriting of the "manager"
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
g.createServerWaitGroup.Wait()
|
||||
}()
|
||||
// Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
|
||||
_ = CloseProvidedListeners()
|
||||
g.notify(readyMsg)
|
||||
|
|
|
@ -150,8 +150,16 @@ func (g *Manager) awaitServer(limit time.Duration) bool {
|
|||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
func() {
|
||||
// FIXME: there is a fundamental design problem of the "manager" and the "wait group".
|
||||
// If nothing has started, the "Wait" just panics: sync: WaitGroup is reused before previous Wait has returned
|
||||
// There is no clear solution besides a complete rewriting of the "manager"
|
||||
defer func() {
|
||||
_ = recover()
|
||||
}()
|
||||
g.createServerWaitGroup.Wait()
|
||||
}()
|
||||
}()
|
||||
if limit > 0 {
|
||||
select {
|
||||
case <-c:
|
||||
|
|
|
@ -233,21 +233,21 @@ func (b *Indexer) Delete(_ context.Context, repoID int64) error {
|
|||
|
||||
// Search searches for files in the specified repo.
|
||||
// Returns the matching file-paths
|
||||
func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
|
||||
func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isFuzzy bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
|
||||
var (
|
||||
indexerQuery query.Query
|
||||
keywordQuery query.Query
|
||||
)
|
||||
|
||||
if isMatch {
|
||||
prefixQuery := bleve.NewPrefixQuery(keyword)
|
||||
prefixQuery.FieldVal = "Content"
|
||||
keywordQuery = prefixQuery
|
||||
} else {
|
||||
if isFuzzy {
|
||||
phraseQuery := bleve.NewMatchPhraseQuery(keyword)
|
||||
phraseQuery.FieldVal = "Content"
|
||||
phraseQuery.Analyzer = repoIndexerAnalyzer
|
||||
keywordQuery = phraseQuery
|
||||
} else {
|
||||
prefixQuery := bleve.NewPrefixQuery(keyword)
|
||||
prefixQuery.FieldVal = "Content"
|
||||
keywordQuery = prefixQuery
|
||||
}
|
||||
|
||||
if len(repoIDs) > 0 {
|
||||
|
|
|
@ -281,10 +281,10 @@ func extractAggs(searchResult *elastic.SearchResult) []*internal.SearchResultLan
|
|||
}
|
||||
|
||||
// Search searches for codes and language stats by given conditions.
|
||||
func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
|
||||
searchType := esMultiMatchTypeBestFields
|
||||
if isMatch {
|
||||
searchType = esMultiMatchTypePhrasePrefix
|
||||
func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isFuzzy bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
|
||||
searchType := esMultiMatchTypePhrasePrefix
|
||||
if isFuzzy {
|
||||
searchType = esMultiMatchTypeBestFields
|
||||
}
|
||||
|
||||
kwQuery := elastic.NewMultiMatchQuery(keyword, "content").Type(searchType)
|
||||
|
|
|
@ -70,7 +70,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
|
|||
|
||||
for _, kw := range keywords {
|
||||
t.Run(kw.Keyword, func(t *testing.T) {
|
||||
total, res, langs, err := indexer.Search(context.TODO(), kw.RepoIDs, "", kw.Keyword, 1, 10, false)
|
||||
total, res, langs, err := indexer.Search(context.TODO(), kw.RepoIDs, "", kw.Keyword, 1, 10, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, kw.IDs, int(total))
|
||||
assert.Len(t, langs, kw.Langs)
|
||||
|
|
|
@ -16,7 +16,7 @@ type Indexer interface {
|
|||
internal.Indexer
|
||||
Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *RepoChanges) error
|
||||
Delete(ctx context.Context, repoID int64) error
|
||||
Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error)
|
||||
Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isFuzzy bool) (int64, []*SearchResult, []*SearchResultLanguages, error)
|
||||
}
|
||||
|
||||
// NewDummyIndexer returns a dummy indexer
|
||||
|
@ -38,6 +38,6 @@ func (d *dummyIndexer) Delete(ctx context.Context, repoID int64) error {
|
|||
return fmt.Errorf("indexer is not ready")
|
||||
}
|
||||
|
||||
func (d *dummyIndexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) {
|
||||
func (d *dummyIndexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isFuzzy bool) (int64, []*SearchResult, []*SearchResultLanguages, error) {
|
||||
return 0, nil, nil, fmt.Errorf("indexer is not ready")
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue