Compare commits

..

39 commits

Author SHA1 Message Date
26a7becd56 nulo: woodpecker CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-10 00:16:25 -03:00
6f5e340458 Dockerfile: rename user to _gitea instead of git 2023-09-10 00:11:52 -03:00
Earl Warren
b63df8b5c4
[SEMVER] 5.0.3+0-gitea-1.20.4 2023-09-08 09:36:09 +02:00
techknowlogick
11af4c9aad
1.20.4 changelog (#26966)
(cherry picked from commit 4a886de71ee21093ec63e39d8a228b7632526eae)
2023-09-08 08:10:08 +02:00
techknowlogick
052c83393f
Improve LDAP group config documentation (#21227) (#26921)
backport #21227

author @svenseeberg

Co-authored-by: Sven Seeberg <mail@sven-seeberg.de>
Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit b6fd1e48c0bf8a19853be8e88d9d4cd7acc99683)
2023-09-08 08:10:08 +02:00
Giteabot
e25033ef8a
Update documents to fix some links (#26885) (#26888)
Backport #26885 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
(cherry picked from commit 9f14b2173abfe9269d8464679e878bc4f00b7a18)
2023-09-08 08:09:18 +02:00
CaiCandong
f34f2c3141
Update docs about attachment path (#26883) (#26884)
Backport #26883
This change was caused by #26271, for configuration as below:
```
[attachment]
ENABLE = true
PATH = data/attachments
MAX_SIZE = 100
MAX_FILES = 5
```
Before #26271, the resolved path is ${AppWorkPath}/${attachments.PATH}
(such as `/var/lib/gitea/data/attachments`)
After #26271, the resolved path is ${AppDataPath}/${attachments.PATH}
(such as `/var/lib/gitea/data/data/attachments`)

Fix https://github.com/go-gitea/gitea/issues/26864 Follow
https://github.com/go-gitea/gitea/pull/26271

(cherry picked from commit e15794f62f4d2f6d0c35117f4240941a31caf3c1)
2023-09-08 08:09:18 +02:00
Giteabot
4df75c254f
Fix wrong review requested number (#26784) (#26880)
Backport #26784 by @lng2020

Fix the wrong review requested number mentioned by #18808 .
Fix #18808
Before:

![ksnip_20230829-140750](https://github.com/go-gitea/gitea/assets/70063547/0af2055b-6f16-4699-a944-c7186831d7f9)
After:

![ksnip_20230829-141817](https://github.com/go-gitea/gitea/assets/70063547/16633264-20ba-45e3-bfbb-a495ed76a45b)

Co-authored-by: Nanguan Lin <70063547+lng2020@users.noreply.github.com>
(cherry picked from commit 2a184796b564309531f709e37430a21d8a3cc2ea)
2023-09-08 08:09:18 +02:00
Giteabot
1689b3da55
Redirect from {repo}/issues/new to {repo}/issues/new/choose when blank issues are disabled (#26813) (#26847)
Backport #26813 by @JakobDev

You can currently visit `{repo}/issues/new` and create a blank issue,
even if it's disabled. This PR fixes this,

Fixes https://codeberg.org/forgejo/forgejo/issues/1356

Co-authored-by: JakobDev <jakobdev@gmx.de>
(cherry picked from commit 2cfabb68ffb4fe188cdbb323be46b300c85f0134)
2023-09-08 08:09:18 +02:00
wxiaoguang
9c0380fe84
Avoid double-unescaping of form value (#26853) (#26863)
Backport #26853

The old `prepareQueryArg` did double-unescaping of form value.

(cherry picked from commit e8da63c24ef9b950999364a86c3a01de6f460e4c)
2023-09-08 08:09:18 +02:00
Giteabot
193e04c43b
Fix verifyCommits error when push a new branch (#26664) (#26810)
Backport #26664 by @CaiCandong

> ### Description
> If a new branch is pushed, and the repository has a rule that would
require signed commits for the new branch, the commit is rejected with a
500 error regardless of whether it's signed.
>
> When pushing a new branch, the "old" commit is the empty ID
(0000000000000000000000000000000000000000). verifyCommits has no
provision for this and passes an invalid commit range to git rev-list.
Prior to 1.19 this wasn't an issue because only pre-existing individual
branches could be protected.
>
> I was able to reproduce with
[try.gitea.io/CraigTest/test](https://try.gitea.io/CraigTest/test),
which is set up with a blanket rule to require commits on all branches.

Fix #25565
Very thanks to @Craig-Holmquist-NTI for reporting the bug and suggesting
an valid solution!

Co-authored-by: CaiCandong <50507092+CaiCandong@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
(cherry picked from commit 93c36f395cf217b44e1f5a529c795a6202df8989)
2023-09-08 08:09:18 +02:00
Giteabot
6b5ef0fad7
Sync tags when adopting repos (#26816) (#26834)
Backport #26816 by @Zettat123

Fixes #26138

Sync the tags into database when adopting repos

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
(cherry picked from commit 302c03c4a9e288bdbda2cd6cde29003d0d13207d)
2023-09-08 08:09:18 +02:00
Giteabot
d5845521a8
check blocklist for emails when adding them to account (#26812) (#26831)
Backport #26812 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
(cherry picked from commit 41bae29f84c95f9af3fd7f24fe3d5b62d36573c5)
2023-09-08 08:09:18 +02:00
yp05327
a6c2201dd4
Fix context filter has no effect in dashboard (#26695) (#26811)
Backport #26695

(cherry picked from commit c72f6067b361f55a1e075e158c5897416cf90d57)
2023-09-08 08:09:18 +02:00
js6pak
833cf722ab
Include the GITHUB_TOKEN/GITEA_TOKEN secret for fork pull requests (#26759) (#26806)
Backport #26759

Co-authored-by: Jason Song <i@wolfogre.com>
(cherry picked from commit 54cc459ea811568ba1d049e6dce22159ac48ffa8)
2023-09-08 08:09:18 +02:00
Giteabot
0b1175f21b
Add fix incorrect can_create_org_repo for org owner team (#26683) (#26791)
Backport #26683 by @yp05327

Related to: #8312 #26491

In migration v109, we only added a new column `CanCreateOrgRepo` in Team
table, but not initial the value of it.
This may cause bug like #26491.

Co-authored-by: yp05327 <576951401@qq.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit c3d323fd859539678ac10988945cbf7af057f766)
2023-09-08 08:09:18 +02:00
Giteabot
4d2b4008d3
Fix some slice append usages (#26778) (#26798)
Backport #26778 by @harryzcy

Co-authored-by: Chongyi Zheng <git@zcy.dev>
Co-authored-by: delvh <dev.lh@web.de>
(cherry picked from commit 4013f3f6006076fa3ca8be191750d1eec5da754d)
2023-09-08 08:09:18 +02:00
Giteabot
1d228e6ee9
Fix being unable to use a repo that prohibits accepting PRs as a PR source. (#26785) (#26790)
Backport #26785 by @CaiCandong

## Description
Sometimes, we need to use an upstream mirror repository to update the
current development repository, but mirror repositories are prohibited
from PR. It should not appear in `merge to,` but it can appear in `pull
from.`
Fix #24585 #26193 #26781
Related #24183

Many thanks to @apnote  for assisting me in reproducing this bug!

## ScreenShot
---
### Before

<img
src="https://github.com/go-gitea/gitea/assets/50507092/3d76c376-1f54-45b9-80c9-6ba8319d6a9a"
width="400px">

<img
src="https://github.com/go-gitea/gitea/assets/50507092/fbfd9f7f-421f-4a2e-9a3e-f2958bbf3312"
width="400px">

### After

<img
src="https://github.com/go-gitea/gitea/assets/50507092/e6984524-4f61-4310-b795-4d8598bd8963"
width="400px">

<img
src="https://github.com/go-gitea/gitea/assets/50507092/04065b44-78d7-4721-bf31-0f1674150727"
width="400px">

Co-authored-by: CaiCandong <50507092+CaiCandong@users.noreply.github.com>
(cherry picked from commit 3bab20491e60a5bbcc64bef42394dcf427d74308)
2023-09-08 08:09:18 +02:00
xpume
53e4f672a3
Fix Page Not Found error (#26768)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
(cherry picked from commit c8b189eb016e86e91e6700470eb2b89345d0e1eb)
2023-09-08 08:07:19 +02:00
Lunny Xiao
bb84b7565f
Fix bug for ctx usage (#26763)
Fix #26684
Backport #26762

(cherry picked from commit a1cec4141e9b82fbee4e3cbf3d92af749df235ab)
2023-09-08 08:07:19 +02:00
wxiaoguang
66016b3fe3
Fix incorrect "tabindex" attributes (#26733) (#26734)
Backport #26733 manually

Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit 307ee2c044abe62c7e61787a6283e670fb3031ab)
2023-09-08 08:07:19 +02:00
Giteabot
d7aa9fc964
Fix link in mirror docs (#26719) (#26732)
Backport #26719 by @silverwind

Fix hash fragment in this link

Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 2f6c0e65966fd75ea066c266a0f7d3724634ad63)
2023-09-08 08:07:19 +02:00
Giteabot
c407810217
Add matrix to support (#26382) (#26722)
Backport #26382 by @jolheiser

This PR adds our matrix space to the support options and alphabetizes
the list.

I also considered adding our Mastodon, however that isn't as suitable as
the other options because it's just whoever has access to the account vs
a community chat/forum.

Signed-off-by: jolheiser <john.olheiser@gmail.com>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
(cherry picked from commit e6173acac948dd46b3552cc08b40fd298bfc668f)
2023-09-08 08:07:19 +02:00
Giteabot
560ff3ea36
Make issue template field template access correct template data (#26698) (#26709)
Backport #26698 by @wxiaoguang

Regression of #23092, the `{{$field := .}}` was missing during that
refactoring.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 4af872178ebd53ac391939908afa7d95ac311b65)
2023-09-08 08:07:19 +02:00
wxiaoguang
4da20765e8
Backport line height fix (#26708)
Backport the `line-height: normal`, because #26520 was backported

(cherry picked from commit 508c624e996d33ad735327bc4583b990adf94d16)
2023-09-08 08:07:19 +02:00
Giteabot
03b397a408
Prefer variables over subprocesses (#26690) (#26693)
Backport #26690 by @thomas-mc-work

… because it doesn't require a separate shell, spawning a process which
cost unnecessary resources and takes time.

Co-authored-by: Thomas McWork <thomas.mc.work@posteo.de>
(cherry picked from commit ecfed9e298bc8bc29e5a0cde13f15e670d8ab0f7)
2023-09-08 08:07:19 +02:00
Giteabot
c1efe5b104
add mfa doc (#26654) (#26674)
Backport #26654 by @lunny

copy and modified from #14572

> Whilst debating enforcing MFA within our team, I realised there isn't
a lot of context to the side effects of enabling it. Most of us use Git
over HTTP and would need to add a token.

I plan to add another PR that adds a sentence to the UI about needing to
generate a token when enabling MFA if HTTP is to be used.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
(cherry picked from commit 2f4de240c1c53fe38f4fc167931362deff0f3833)
2023-09-08 08:07:19 +02:00
Giteabot
a98cb4d806
update config docs url (#26640) (#26642)
Backport #26640 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
(cherry picked from commit e1fa3d1d696db9f9b48f38254ea61d230f5eefe5)
2023-09-08 08:07:19 +02:00
Giteabot
ef46b01168
Fix unable to display individual-level project (#26198) (#26636)
Backport #26198 by @CaiCandong

As title

Before:

![image](https://github.com/go-gitea/gitea/assets/50507092/94afc3bf-5597-4151-a59b-5632840ffa21)

After:

![image](https://github.com/go-gitea/gitea/assets/50507092/df81aa0b-98a6-477d-a270-2e45b3dca0fc)

fix #26189

Co-authored-by: caicandong <50507092+CaiCandong@users.noreply.github.com>
(cherry picked from commit 352a495c02e079e3b9ee0018dea937e7ccc5692e)
2023-09-08 08:07:19 +02:00
Giteabot
06c45d3b6e
Use correct minio error (#26634) (#26639)
Backport #26634 by @delvh

Previously, `err` was defined above, checked for `err == nil` and used
nowhere else.
Hence, the result of `convertMinioErr` would always be `nil`.
This leads to a NPE further down the line.
That is not intentional, it should convert the error of the most recent
operation, not one of its predecessors.

Found through
https://discord.com/channels/322538954119184384/322538954119184384/1143185780206993550.

Co-authored-by: delvh <dev.lh@web.de>
(cherry picked from commit a4b14638b50593a94121d2dfbca1d848fbec79b9)
2023-09-08 08:07:19 +02:00
a1012112796
5abca17b64
fix reopen logic for agit flow pull request (#26399) (#26613)
Backport #26399

Signed-off-by: a1012112796 <1012112796@qq.com>
Co-authored-by: Giteabot <teabot@gitea.io>
(cherry picked from commit f43df2f8201c33260b65b582556a3b0f4c75b637)
2023-09-08 08:07:19 +02:00
Giteabot
19a49e763a
Add branch_filter to hooks API endpoints (#26599) (#26632)
Backport #26599 by @yardenshoham

We now include the branch filler in the response.

- Closes #26591

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: Yarden Shoham <git@yardenshoham.com>
(cherry picked from commit fe78aabc673daf36655f0cca7e83cf2b057b8361)
2023-09-08 08:07:19 +02:00
Giteabot
2f6d011503
Ignore the trailing slashes when comparing oauth2 redirect_uri (#26597) (#26618)
Backport #26597 by @wxiaoguang

Fix #26526

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
(cherry picked from commit 4aed0e6b074b538ba71ba0560a416f282b3cc30b)
2023-09-08 08:07:19 +02:00
Gusted
ec4b6d7d04 Merge pull request '[BRANDING] gitea logo for gitea webhooks' (#1369) from earl-warren/forgejo:wip-v1.20-webhook into v1.20/forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1369
2023-09-01 19:26:36 +00:00
Earl Warren
7f7e1ccab8
[BRANDING] gitea logo for gitea webhooks
Refs: https://codeberg.org/forgejo/forgejo/issues/1367
(cherry picked from commit 2d8c1b93734acec918729e27c5613a8f9fb41ceb)
2023-09-01 11:56:05 +02:00
Earl Warren
18b4554009 Merge pull request '[TESTS] [v1.20] verify facts for the admin storage documentation' (#1364) from earl-warren/forgejo:wip-v1.20-development-storage-doc-tests into v1.20/forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1364
2023-08-31 15:03:31 +00:00
Earl Warren
d3b8870700
[TESTS] verify facts for the admin storage documentation (squash)
(cherry picked from commit d83d8ce57b8b39b4da849f5403198ecf706117ba)
2023-08-31 15:32:22 +02:00
oliverpool
ebf80c3d90 [CI] update DNS on experimental release (#1298)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1298
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
2023-08-22 09:02:58 +02:00
Gusted
fa25b9eec6
[GITEA] Add slow SQL query warning
- Backport of https://codeberg.org/forgejo/forgejo/pulls/1284
  - Databases are one of the most important parts of Forgejo, every
interaction with Forgejo uses the database in one way or another.
Therefore, it is important to maintain the database and recognize when
Forgejo is not doing well with the database. Forgejo already has the
option to log *every* SQL query along with its execution time, but
monitoring becomes impractical for larger instances and takes up
unnecessary storage in the logs.
  - Add a QoL enhancement that allows instance administrators to specify a
threshold value beyond which query execution time is logged as a warning
in the xorm logger. The default value is a conservative five seconds to
avoid this becoming a source of spam in the logs.
  - The use case for this patch is that with an instance the size of Codeberg, monitoring SQL logs is not very fruitful and most of them are uninteresting. Recently, in the context of persistent deadlock issues (https://codeberg.org/forgejo/forgejo/issues/220), I have noticed that certain queries hold locks on tables like comment and issue for several seconds. This patch helps to identify which queries these are and when they happen.
  - Added unit test.
2023-08-21 21:18:43 +02:00
111 changed files with 1042 additions and 249 deletions

View file

@ -58,3 +58,22 @@ jobs:
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }} gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
verbose: ${{ secrets.VERBOSE }} verbose: ${{ secrets.VERBOSE }}
- name: set up go for the DNS update below
uses: https://code.forgejo.org/actions/setup-go@v4
if: secrets.ROLE == 'forgejo-experimental'
with:
go-version: ">=1.21"
check-latest: true
- name: update the _release.experimental DNS record
if: secrets.ROLE == 'forgejo-experimental'
uses: https://code.forgejo.org/actions/ovh-dns-update@v1
with:
subdomain: _release.experimental
domain: forgejo.com # there is a CNAME from .org to .com (for security reasons)
record-id: 5283602601
value: v=${{ github.ref_name }}
ovh-app-key: ${{ secrets.OVH_APP_KEY }}
ovh-app-secret: ${{ secrets.OVH_APP_SECRET }}
ovh-consumer-key: ${{ secrets.OVH_CON_KEY }}

15
.woodpecker.yml Normal file
View file

@ -0,0 +1,15 @@
pipeline:
nulo-container:
image: docker.io/woodpeckerci/plugin-docker-buildx
settings:
repo: gitea.nulo.in/Nulo/forgejo
tag: v1.20.4-0
registry: https://gitea.nulo.in
username: Nulo
password:
from_secret: registry_secret
secrets: [REGISTRY_SECRET]
when:
branch: "nulo/release/v1.20"
event: "push"

View file

@ -4,6 +4,34 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com). been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.20.4](https://github.com/go-gitea/gitea/releases/tag/1.20.4) - 2023-09-08
* SECURITY
* Check blocklist for emails when adding them to account (#26812) (#26831)
* ENHANCEMENTS
* Add `branch_filter` to hooks API endpoints (#26599) (#26632)
* Fix incorrect "tabindex" attributes (#26733) (#26734)
* Use line-height: normal by default (#26635) (#26708)
* Fix unable to display individual-level project (#26198) (#26636)
* BUGFIXES
* Fix wrong review requested number (#26784) (#26880)
* Avoid double-unescaping of form value (#26853) (#26863)
* Redirect from `{repo}/issues/new` to `{repo}/issues/new/choose` when blank issues are disabled (#26813) (#26847)
* Sync tags when adopting repos (#26816) (#26834)
* Fix verifyCommits error when push a new branch (#26664) (#26810)
* Include the GITHUB_TOKEN/GITEA_TOKEN secret for fork pull requests (#26759) (#26806)
* Fix some slice append usages (#26778) (#26798)
* Add fix incorrect can_create_org_repo for org owner team (#26683) (#26791)
* Fix bug for ctx usage (#26763)
* Make issue template field template access correct template data (#26698) (#26709)
* Use correct minio error (#26634) (#26639)
* Ignore the trailing slashes when comparing oauth2 redirect_uri (#26597) (#26618)
* Set errwriter for urfave/cli v1 (#26616)
* Fix reopen logic for agit flow pull request (#26399) (#26613)
* Fix context filter has no effect in dashboard (#26695) (#26811)
* Fix being unable to use a repo that prohibits accepting PRs as a PR source. (#26785) (#26790)
* Fix Page Not Found error (#26768)
## [1.20.3](https://github.com/go-gitea/gitea/releases/tag/v1.20.3) - 2023-08-20 ## [1.20.3](https://github.com/go-gitea/gitea/releases/tag/v1.20.3) - 2023-08-20
* BREAKING * BREAKING

View file

@ -89,7 +89,7 @@ endif
VERSION = ${GITEA_VERSION} VERSION = ${GITEA_VERSION}
# SemVer # SemVer
FORGEJO_VERSION := 5.0.2+0-gitea-1.20.3 FORGEJO_VERSION := 5.0.3+0-gitea-1.20.4
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" -X "code.gitea.io/gitea/routers/api/forgejo/v1.ForgejoVersion=$(FORGEJO_VERSION)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)" LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" -X "code.gitea.io/gitea/routers/api/forgejo/v1.ForgejoVersion=$(FORGEJO_VERSION)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)"

View file

@ -4,7 +4,7 @@
;; Do not copy the whole file as-is, as it contains some invalid sections for illustrative purposes. ;; Do not copy the whole file as-is, as it contains some invalid sections for illustrative purposes.
;; If you don't know what a setting is you should not set it. ;; If you don't know what a setting is you should not set it.
;; ;;
;; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation. ;; see https://docs.gitea.com/administration/config-cheat-sheet for additional documentation.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1804,8 +1804,9 @@ LEVEL = Info
;; Currently, only `minio` is supported. ;; Currently, only `minio` is supported.
;SERVE_DIRECT = false ;SERVE_DIRECT = false
;; ;;
;; Path for attachments. Defaults to `data/attachments` only available when STORAGE_TYPE is `local` ;; Path for attachments. Defaults to `attachments`. Only available when STORAGE_TYPE is `local`
;PATH = data/attachments ;; Relative paths will be resolved to `${AppDataPath}/${attachment.PATH}`
;PATH = attachments
;; ;;
;; Minio endpoint to connect only available when STORAGE_TYPE is `minio` ;; Minio endpoint to connect only available when STORAGE_TYPE is `minio`
;MINIO_ENDPOINT = localhost:9000 ;MINIO_ENDPOINT = localhost:9000

View file

@ -818,7 +818,7 @@ Default templates for project boards:
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once. - `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]` - `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]`
- `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing. - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
- `PATH`: **data/attachments**: Path to store attachments only available when STORAGE_TYPE is `local` - `PATH`: **attachments**: Path to store attachments only available when STORAGE_TYPE is `local`, relative paths will be resolved to `${AppDataPath}/${attachment.PATH}`.
- `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when STORAGE_TYPE is `minio` - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when STORAGE_TYPE is `minio`
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio` - `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_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`

View file

@ -230,7 +230,7 @@ menu:
- `MAX_SIZE`: 附件最大限制,单位 MB比如 `4` - `MAX_SIZE`: 附件最大限制,单位 MB比如 `4`
- `MAX_FILES`: 一次最多上传的附件数量,比如: `5` - `MAX_FILES`: 一次最多上传的附件数量,比如: `5`
- `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。 - `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。
- `PATH`: **data/attachments**: 附件存储路径,仅当 `STORAGE_TYPE``local` 时有效。 - `PATH`: **attachments**: 存储附件的路径,仅当 STORAGE_TYPE 为 `local` 时可用。如果是相对路径,将会被解析为 `${AppDataPath}/${attachment.PATH}`.
- `MINIO_ENDPOINT`: **localhost:9000**: Minio 终端,仅当 `STORAGE_TYPE``minio` 时有效。 - `MINIO_ENDPOINT`: **localhost:9000**: Minio 终端,仅当 `STORAGE_TYPE``minio` 时有效。
- `MINIO_ACCESS_KEY_ID`: Minio accessKeyID ,仅当 `STORAGE_TYPE``minio` 时有效。 - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID ,仅当 `STORAGE_TYPE``minio` 时有效。
- `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `STORAGE_TYPE``minio` 时有效。 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey仅当 `STORAGE_TYPE``minio` 时有效。

View file

@ -24,7 +24,7 @@ it is just a matter of:
- add some configuration to your `app.ini` file - add some configuration to your `app.ini` file
- restart your Gitea instance - restart your Gitea instance
This supports rendering of whole files. If you want to render code blocks in markdown you would need to do something with javascript. See some examples on the [Customizing Gitea](../customizing-gitea) page. This supports rendering of whole files. If you want to render code blocks in markdown you would need to do something with javascript. See some examples on the [Customizing Gitea](administration/customizing-gitea.md) page.
## Installing external binaries ## Installing external binaries

View file

@ -36,7 +36,7 @@ KEY_FILE = key.pem
``` ```
Note that if your certificate is signed by a third party certificate authority (i.e. not self-signed), then cert.pem should contain the certificate chain. The server certificate must be the first entry in cert.pem, followed by the intermediaries in order (if any). The root certificate does not have to be included because the connecting client must already have it in order to estalbish the trust relationship. Note that if your certificate is signed by a third party certificate authority (i.e. not self-signed), then cert.pem should contain the certificate chain. The server certificate must be the first entry in cert.pem, followed by the intermediaries in order (if any). The root certificate does not have to be included because the connecting client must already have it in order to estalbish the trust relationship.
To learn more about the config values, please checkout the [Config Cheat Sheet](../config-cheat-sheet#server-server). To learn more about the config values, please checkout the [Config Cheat Sheet](administration/config-cheat-sheet.md#server-server).
For the `CERT_FILE` or `KEY_FILE` field, the file path is relative to the `GITEA_CUSTOM` environment variable when it is a relative path. It can be an absolute path as well. For the `CERT_FILE` or `KEY_FILE` field, the file path is relative to the `GITEA_CUSTOM` environment variable when it is a relative path. It can be an absolute path as well.
@ -85,11 +85,11 @@ ACME_DIRECTORY=https
ACME_EMAIL=email@example.com ACME_EMAIL=email@example.com
``` ```
To learn more about the config values, please checkout the [Config Cheat Sheet](../config-cheat-sheet#server-server). To learn more about the config values, please checkout the [Config Cheat Sheet](administration/config-cheat-sheet.md#server-server).
## Using a reverse proxy ## Using a reverse proxy
Setup up your reverse proxy as shown in the [reverse proxy guide](../reverse-proxies). Setup up your reverse proxy as shown in the [reverse proxy guide](administration/reverse-proxies.md).
After that, enable HTTPS by following one of these guides: After that, enable HTTPS by following one of these guides:

View file

@ -82,11 +82,11 @@ ACME_DIRECTORY=https
ACME_EMAIL=email@example.com ACME_EMAIL=email@example.com
``` ```
要了解关于配置, 请访问 [配置备忘单](../config-cheat-sheet#server-server)获取更多信息 要了解关于配置, 请访问 [配置备忘单](administration/config-cheat-sheet.md#server-server)获取更多信息
## 使用反向代理服务器 ## 使用反向代理服务器
按照 [reverse proxy guide](../reverse-proxies) 的规则设置你的反向代理服务器 按照 [reverse proxy guide](administration/reverse-proxies.md) 的规则设置你的反向代理服务器
然后,按照下面的向导启用 HTTPS 然后,按照下面的向导启用 HTTPS

View file

@ -20,6 +20,8 @@ menu:
- [Paid Commercial Support](https://about.gitea.com/) - [Paid Commercial Support](https://about.gitea.com/)
- [Discord](https://discord.gg/Gitea) - [Discord](https://discord.gg/Gitea)
- [Discourse Forum](https://discourse.gitea.io/) - [Discourse Forum](https://discourse.gitea.io/)
- [Matrix](https://matrix.to/#/#gitea-space:matrix.org)
- NOTE: Most of the Matrix channels are bridged with their counterpart in Discord and may experience some degree of flakiness with the bridge process.
**NOTE:** When asking for support, it may be a good idea to have the following available so that the person helping has all the info they need: **NOTE:** When asking for support, it may be a good idea to have the following available so that the person helping has all the info they need:

View file

@ -117,7 +117,7 @@ chmod 770 /etc/gitea
- 使用 `gitea generate secret` 创建 `SECRET_KEY``INTERNAL_TOKEN` - 使用 `gitea generate secret` 创建 `SECRET_KEY``INTERNAL_TOKEN`
- 提供所有必要的密钥 - 提供所有必要的密钥
详情参考 [命令行文档](/zh-cn/command-line/) 中有关 `gitea generate secret` 的内容。 详情参考 [命令行文档](administration/command-line.md) 中有关 `gitea generate secret` 的内容。
### 配置 Gitea 工作路径 ### 配置 Gitea 工作路径

View file

@ -52,7 +52,7 @@ git checkout v@version@
- `go` @minGoVersion@ 或以上版本, 详见[这里](https://golang.google.cn/doc/install) - `go` @minGoVersion@ 或以上版本, 详见[这里](https://golang.google.cn/doc/install)
- `node` @minNodeVersion@ 或以上版本,并且安装 `npm`, 详见[这里](https://nodejs.org/zh-cn/download/) - `node` @minNodeVersion@ 或以上版本,并且安装 `npm`, 详见[这里](https://nodejs.org/zh-cn/download/)
- `make`, 详见[这里](/zh-cn/hacking-on-gitea/) - `make`, 详见[这里](development/hacking-on-gitea.md)
各种可用的 [make 任务](https://github.com/go-gitea/gitea/blob/main/Makefile) 各种可用的 [make 任务](https://github.com/go-gitea/gitea/blob/main/Makefile)
可以用来使编译过程更方便。 可以用来使编译过程更方便。

View file

@ -81,7 +81,7 @@ docker run --entrypoint="" --rm -it gitea/act_runner:latest act_runner generate-
When you are using the docker image, you can specify the configuration file by using the `CONFIG_FILE` environment variable. Make sure that the file is mounted into the container as a volume: When you are using the docker image, you can specify the configuration file by using the `CONFIG_FILE` environment variable. Make sure that the file is mounted into the container as a volume:
```bash ```bash
docker run -v $(pwd)/config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml ... docker run -v $PWD/config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml ...
``` ```
You may notice the commands above are both incomplete, because it is not the time to run the act runner yet. You may notice the commands above are both incomplete, because it is not the time to run the act runner yet.
@ -157,8 +157,8 @@ If you are using the docker image, behaviour will be slightly different. Registr
```bash ```bash
docker run \ docker run \
-v $(pwd)/config.yaml:/config.yaml \ -v $PWD/config.yaml:/config.yaml \
-v $(pwd)/data:/data \ -v $PWD/data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
-e CONFIG_FILE=/config.yaml \ -e CONFIG_FILE=/config.yaml \
-e GITEA_INSTANCE_URL=<instance_url> \ -e GITEA_INSTANCE_URL=<instance_url> \

View file

@ -157,12 +157,13 @@ Uses the following fields:
- User Attribute in Group (optional) - User Attribute in Group (optional)
- Which user LDAP attribute is listed in the group. - The user attribute that is used to reference a user in the group object.
- Example: `uid` - Example: `uid` if the group objects contains a `member: bender` and the user object contains a `uid: bender`.
- Example: `dn` if the group object contains a `member: uid=bender,ou=users,dc=planetexpress,dc=com`.
- Group Attribute for User (optional) - Group Attribute for User (optional)
- Which group LDAP attribute contains an array above user attribute names. - The attribute of the group object that lists/contains the group members.
- Example: `memberUid` - Example: `memberUid` or `member`
## PAM (Pluggable Authentication Module) ## PAM (Pluggable Authentication Module)

View file

@ -27,7 +27,7 @@ menu:
标签具有必填的名称和颜色,可选的描述,以及必须是独占的或非独占的(见下面的“作用域标签”)。 标签具有必填的名称和颜色,可选的描述,以及必须是独占的或非独占的(见下面的“作用域标签”)。
当您创建一个仓库时,可以通过使用 `工单标签Issue Labels` 选项来选择标签集。该选项列出了一些在您的实例上 [全局配置的可用标签集](../administration/customizing-gitea/#labels)。在创建仓库时,这些标签也将被创建。 当您创建一个仓库时,可以通过使用 `工单标签Issue Labels` 选项来选择标签集。该选项列出了一些在您的实例上 [全局配置的可用标签集](administration/customizing-gitea.md#labels)。在创建仓库时,这些标签也将被创建。
## 作用域标签 ## 作用域标签

View file

@ -94,7 +94,7 @@ Sometimes a commit or pull request may fix or bring back a problem documented
in a particular issue. Gitea supports closing and reopening the referenced in a particular issue. Gitea supports closing and reopening the referenced
issues by preceding the reference with a particular _keyword_. Common keywords issues by preceding the reference with a particular _keyword_. Common keywords
include "closes", "fixes", "reopens", etc. This list can be include "closes", "fixes", "reopens", etc. This list can be
[customized]({{< ref "doc/administration/config-cheat-sheet.en-us.md" >}}) by the [customized](administration/config-cheat-sheet.md) by the
site administrator. site administrator.
Example: Example:

View file

@ -66,7 +66,7 @@ menu:
## 可操作的引用在合并请求和提交消息中 ## 可操作的引用在合并请求和提交消息中
有时一个提交或合并请求可能会修复或重新出现在某个特定工单中。Gitea 支持在引用之前加上特定的“关键字”来关闭和重新打开被引用的工单。常见的关键字包括“closes”、“fixes”、“reopens”等。这个列表可以由站点管理员进行 [自定义]({{< ref "doc/administration/config-cheat-sheet.zh-cn.md" >}})。 有时一个提交或合并请求可能会修复或重新出现在某个特定工单中。Gitea 支持在引用之前加上特定的“关键字”来关闭和重新打开被引用的工单。常见的关键字包括“closes”、“fixes”、“reopens”等。这个列表可以由站点管理员进行 [自定义](administration/config-cheat-sheet.md)。
示例: 示例:

View file

@ -0,0 +1,35 @@
---
date: "2023-08-22T14:21:00+08:00"
title: "Multi-factor Authentication (MFA)"
slug: "multi-factor-authentication"
weight: 15
toc: false
draft: false
menu:
sidebar:
parent: "usage"
name: "Multi-factor Authentication (MFA)"
weight: 15
identifier: "multi-factor-authentication"
---
# Multi-factor Authentication (MFA)
Multi-factor Authentication (also referred to as MFA or 2FA) enhances security by requiring a time-sensitive set of credentials in addition to a password.
If a password were later to be compromised, logging into Gitea will not be possible without the additional credentials and the account would remain secure.
Gitea supports both TOTP (Time-based One-Time Password) tokens and FIDO-based hardware keys using the Webauthn API.
MFA can be configured within the "Security" tab of the user settings page.
## MFA Considerations
Enabling MFA on a user does affect how the Git HTTP protocol can be used with the Git CLI.
This interface does not support MFA, and trying to use a password normally will no longer be possible whilst MFA is enabled.
If SSH is not an option for Git operations, an access token can be generated within the "Applications" tab of the user settings page.
This access token can be used as if it were a password in order to allow the Git CLI to function over HTTP.
> **Warning** - By its very nature, an access token sidesteps the security benefits of MFA.
> It must be kept secure and should only be used as a last resort.
The Gitea API supports providing the relevant TOTP password in the `X-Gitea-OTP` header, as described in [API Usage](development/api-usage.md).
This should be used instead of an access token where possible.

View file

@ -66,4 +66,4 @@ The first value of the list will be used in helpers.
## Pull Request Templates ## Pull Request Templates
You can find more information about pull request templates at the page [Issue and Pull Request templates](issue-pull-request-templates). You can find more information about pull request templates at the page [Issue and Pull Request templates](usage/issue-pull-request-templates.md).

View file

@ -30,4 +30,4 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
## 合并请求模板 ## 合并请求模板
有关合并请求模板的更多信息请您移步 : [工单与合并请求模板](issue-pull-request-templates) 有关合并请求模板的更多信息请您移步 : [工单与合并请求模板](usage/issue-pull-request-templates.md)

View file

@ -31,4 +31,4 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
## 合併請求範本 ## 合併請求範本
您可以在[問題與合併請求範本](issue-pull-request-templates)找到更多關於合併請求範本的資訊。 您可以在[問題與合併請求範本](usage/issue-pull-request-templates.md)找到更多關於合併請求範本的資訊。

View file

@ -53,6 +53,15 @@ func (app *OAuth2Application) TableName() string {
// ContainsRedirectURI checks if redirectURI is allowed for app // ContainsRedirectURI checks if redirectURI is allowed for app
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
contains := func(s string) bool {
s = strings.TrimSuffix(strings.ToLower(s), "/")
for _, u := range app.RedirectURIs {
if strings.TrimSuffix(strings.ToLower(u), "/") == s {
return true
}
}
return false
}
if !app.ConfidentialClient { if !app.ConfidentialClient {
uri, err := url.Parse(redirectURI) uri, err := url.Parse(redirectURI)
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3 // ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
@ -61,13 +70,13 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
if ip != nil && ip.IsLoopback() { if ip != nil && ip.IsLoopback() {
// strip port // strip port
uri.Host = uri.Hostname() uri.Host = uri.Hostname()
if util.SliceContainsString(app.RedirectURIs, uri.String(), true) { if contains(uri.String()) {
return true return true
} }
} }
} }
} }
return util.SliceContainsString(app.RedirectURIs, redirectURI, true) return contains(redirectURI)
} }
// Base32 characters, but lowercased. // Base32 characters, but lowercased.

View file

@ -63,6 +63,18 @@ func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
assert.False(t, app.ContainsRedirectURI(":")) assert.False(t, app.ContainsRedirectURI(":"))
} }
func TestOAuth2Application_ContainsRedirect_Slash(t *testing.T) {
app := &auth_model.OAuth2Application{RedirectURIs: []string{"http://127.0.0.1"}}
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1"))
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
assert.False(t, app.ContainsRedirectURI("http://127.0.0.1/other"))
app = &auth_model.OAuth2Application{RedirectURIs: []string{"http://127.0.0.1/"}}
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1"))
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
assert.False(t, app.ContainsRedirectURI("http://127.0.0.1/other"))
}
func TestOAuth2Application_ValidateClientSecret(t *testing.T) { func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1}) app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})

View file

@ -11,10 +11,13 @@ import (
"io" "io"
"reflect" "reflect"
"strings" "strings"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/contexts"
"xorm.io/xorm/names" "xorm.io/xorm/names"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
@ -147,6 +150,13 @@ func InitEngine(ctx context.Context) error {
xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
xormEngine.SetDefaultContext(ctx) xormEngine.SetDefaultContext(ctx)
if setting.Database.SlowQueryTreshold > 0 {
xormEngine.AddHook(&SlowQueryHook{
Treshold: setting.Database.SlowQueryTreshold,
Logger: log.GetLogger("xorm"),
})
}
SetDefaultEngine(ctx, xormEngine) SetDefaultEngine(ctx, xormEngine)
return nil return nil
} }
@ -300,3 +310,21 @@ func SetLogSQL(ctx context.Context, on bool) {
sess.Engine().ShowSQL(on) sess.Engine().ShowSQL(on)
} }
} }
type SlowQueryHook struct {
Treshold time.Duration
Logger log.Logger
}
var _ contexts.Hook = &SlowQueryHook{}
func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
return c.Ctx, nil
}
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
if c.ExecuteTime >= h.Treshold {
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
}
return nil
}

View file

@ -6,15 +6,19 @@ package db_test
import ( import (
"path/filepath" "path/filepath"
"testing" "testing"
"time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys _ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"xorm.io/xorm"
) )
func TestDumpDatabase(t *testing.T) { func TestDumpDatabase(t *testing.T) {
@ -85,3 +89,37 @@ func TestPrimaryKeys(t *testing.T) {
} }
} }
} }
func TestSlowQuery(t *testing.T) {
lc, cleanup := test.NewLogChecker("slow-query")
lc.StopMark("[Slow SQL Query]")
defer cleanup()
e := db.GetEngine(db.DefaultContext)
engine, ok := e.(*xorm.Engine)
assert.True(t, ok)
// It's not possible to clean this up with XORM, but it's luckily not harmful
// to leave around.
engine.AddHook(&db.SlowQueryHook{
Treshold: time.Second * 10,
Logger: log.GetLogger("slow-query"),
})
// NOOP query.
e.Exec("SELECT 1 WHERE false;")
_, stopped := lc.Check(100 * time.Millisecond)
assert.False(t, stopped)
engine.AddHook(&db.SlowQueryHook{
Treshold: 0, // Every query should be logged.
Logger: log.GetLogger("slow-query"),
})
// NOOP query.
e.Exec("SELECT 1 WHERE false;")
_, stopped = lc.Check(100 * time.Millisecond)
assert.True(t, stopped)
}

View file

@ -277,3 +277,11 @@
lower_email: user2-2@example.com lower_email: user2-2@example.com
is_activated: false is_activated: false
is_primary: false is_primary: false
-
id: 36
uid: 36
email: abcde@gitea.com
lower_email: abcde@gitea.com
is_activated: true
is_primary: false

View file

@ -1 +1,23 @@
[] # empty -
id: 5
owner_id: 36
key_id: B15431642629B826
primary_key_id:
content: xsDNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eafTlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCOnKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/jdE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1rGGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWpCm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfHE2TGjzjQzgChfmcAEQEAAQ==
verified: true
can_sign: true
can_encrypt_comms: true
can_encrypt_storage: true
can_certify: true
-
id: 6
owner_id: 36
key_id: EE3AF48454AFD619
primary_key_id: B15431642629B826
content: zsDNBGTrY3UBDADsHrzuOicQaPdUQm0+0UNrs92cESm/j/4yBBUk+sfLZAo6J99c4eh4nAQzzZ7al080rYKB0G+7xoRz1eHcQH6zrVcqB8KYtf/sdY47WaMiMyxM+kTSvzp7tsv7QuSQZ0neUEXRyYMz5ttBfIjWUd+3NDItuHyB+MtNWlS3zXgaUbe5VifqKaNmzN0Ye4yXTKcpypE3AOqPVz+iIFv3c6TmsqLHJaR4VoicCleAqLyF/28WsJO7M9dDW+EM3MZVnsVpycTURyHAJGfSk10waQZAaRwmarCN/q0KEJ+aEAK/SRliUneBZoMO5hY5iBeG432tofwaQqAahPv9uXIb1n2JEMKwnMlMA9UGD1AcDbywfj1m/ZGBBw95i4Ekkfn43RvV3THr7uJU/dRqqP+iic4MwpUrOxqELW/kmeHXlBcNbZZhEEvwRoW7U2/9eeuog4nRleRJ0pi/xOP9wmxkKjaIPIK3phdBtEpVk4w/UTAWNdyIIrFggukeAnZFyGJwlm8AEQEAAQ==
verified: true
can_sign: true
can_encrypt_comms: true
can_encrypt_storage: true
can_certify: true

View file

@ -1301,7 +1301,7 @@
lower_name: limited_org36 lower_name: limited_org36
name: limited_org36 name: limited_org36
full_name: Limited Org 36 full_name: Limited Org 36
email: limited_org36@example.com email: abcde@gitea.com
keep_email_private: false keep_email_private: false
email_notifications_preference: enabled email_notifications_preference: enabled
passwd: ZogKvWdyEx:password passwd: ZogKvWdyEx:password
@ -1320,7 +1320,7 @@
allow_create_organization: true allow_create_organization: true
prohibit_login: false prohibit_login: false
avatar: avatar22 avatar: avatar22
avatar_email: limited_org36@example.com avatar_email: abcde@gitea.com
use_custom_avatar: false use_custom_avatar: false
num_followers: 0 num_followers: 0
num_following: 0 num_following: 0

View file

@ -345,12 +345,21 @@ func applyMentionedCondition(sess *xorm.Session, mentionedID int64) *xorm.Sessio
} }
func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) *xorm.Session { func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) *xorm.Session {
return sess.Join("INNER", []string{"review", "r"}, "issue.id = r.issue_id"). existInTeamQuery := builder.Select("team_user.team_id").
And("issue.poster_id <> ?", reviewRequestedID). From("team_user").
And("r.type = ?", ReviewTypeRequest). Where(builder.Eq{"team_user.uid": reviewRequestedID})
And("r.reviewer_id = ? and r.id in (select max(id) from review where issue_id = r.issue_id and reviewer_id = r.reviewer_id and type in (?, ?, ?))"+
" or r.reviewer_team_id in (select team_id from team_user where uid = ?)", subQuery := builder.Select("review.issue_id").
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID) From("review").
Where(builder.And(
builder.In("review.type", []ReviewType{ReviewTypeRequest, ReviewTypeReject, ReviewTypeApprove}),
builder.Or(
builder.Eq{"review.reviewer_id": reviewRequestedID},
builder.In("review.reviewer_team_id", existInTeamQuery),
),
))
return sess.Where("issue.poster_id <> ?", reviewRequestedID).
And(builder.In("issue.id", subQuery))
} }
func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session { func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session {

View file

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"xorm.io/builder" "xorm.io/builder"
) )
@ -161,7 +162,17 @@ func ValidateEmail(email string) error {
return ErrEmailInvalid{email} return ErrEmailInvalid{email}
} }
// TODO: add an email allow/block list // 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 return nil
} }

View file

@ -4,29 +4,18 @@
package context package context
import ( import (
"net/url"
"strings" "strings"
"time" "time"
) )
// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since // GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) { func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
qCreatedBefore, err := prepareQueryArg(ctx, "before") before, err = parseFormTime(ctx, "before")
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
qCreatedSince, err := prepareQueryArg(ctx, "since") since, err = parseFormTime(ctx, "since")
if err != nil {
return 0, 0, err
}
before, err = parseTime(qCreatedBefore)
if err != nil {
return 0, 0, err
}
since, err = parseTime(qCreatedSince)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
@ -34,7 +23,8 @@ func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
} }
// parseTime parse time and return unix timestamp // parseTime parse time and return unix timestamp
func parseTime(value string) (int64, error) { func parseFormTime(ctx *Base, name string) (int64, error) {
value := strings.TrimSpace(ctx.FormString(name))
if len(value) != 0 { if len(value) != 0 {
t, err := time.Parse(time.RFC3339, value) t, err := time.Parse(time.RFC3339, value)
if err != nil { if err != nil {
@ -46,10 +36,3 @@ func parseTime(value string) (int64, error) {
} }
return 0, nil return 0, nil
} }
// prepareQueryArg unescape and trim a query arg
func prepareQueryArg(ctx *Base, name string) (value string, err error) {
value, err = url.PathUnescape(ctx.FormString(name))
value = strings.TrimSpace(value)
return value, err
}

61
modules/doctor/fix8312.go Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package doctor
import (
"context"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/modules/log"
"xorm.io/builder"
)
func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix bool) error {
count := 0
err := db.Iterate(
ctx,
builder.Eq{"authorize": perm.AccessModeOwner, "can_create_org_repo": false},
func(ctx context.Context, team *org_model.Team) error {
team.CanCreateOrgRepo = true
count++
if !autofix {
return nil
}
return models.UpdateTeam(team, false, false)
},
)
if err != nil {
logger.Critical("Unable to iterate across repounits to fix incorrect can_create_org_repo: Error %v", err)
return err
}
if !autofix {
if count == 0 {
logger.Info("Found no team with incorrect can_create_org_repo")
} else {
logger.Warn("Found %d teams with incorrect can_create_org_repo", count)
}
return nil
}
logger.Info("Fixed %d teams with incorrect can_create_org_repo", count)
return nil
}
func init() {
Register(&Check{
Title: "Check for incorrect can_create_org_repo for org owner teams",
Name: "fix-owner-team-create-org-repo",
IsDefault: false,
Run: fixOwnerTeamCreateOrgRepo,
Priority: 7,
})
}

View file

@ -46,6 +46,7 @@ var (
ConnMaxLifetime time.Duration ConnMaxLifetime time.Duration
IterateBufferSize int IterateBufferSize int
AutoMigration bool AutoMigration bool
SlowQueryTreshold time.Duration
}{ }{
Timeout: 500, Timeout: 500,
IterateBufferSize: 50, IterateBufferSize: 50,
@ -92,6 +93,7 @@ func loadDBSetting(rootCfg ConfigProvider) {
Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10) Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second) Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true) Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
Database.SlowQueryTreshold = sec.Key("SLOW_QUERY_TRESHOLD").MustDuration(5 * time.Second)
} }
// DBConnStr returns database connection string // DBConnStr returns database connection string

View file

@ -0,0 +1,263 @@
// SPDX-License-Identifier: MIT
//
// Tests verifying the Forgejo documentation on storage settings is correct
//
// https://forgejo.org/docs/v1.20/admin/storage/
//
package setting
import (
"fmt"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestForgejoDocs_StorageTypes(t *testing.T) {
iniStr := `
[server]
APP_DATA_PATH = /
`
testStorageTypesDefaultAndSpecificStorage(t, iniStr)
}
func testStorageGetPath(storage *Storage) string {
if storage.Type == MinioStorageType {
return storage.MinioConfig.BasePath
}
return storage.Path
}
var testSectionToBasePath = map[string]string{
"attachment": "attachments",
"lfs": "lfs",
"avatar": "avatars",
"repo-avatar": "repo-avatars",
"repo-archive": "repo-archive",
"packages": "packages",
"storage.actions_log": "actions_log",
"actions.artifacts": "actions_artifacts",
}
type testSectionToPathFun func(StorageType, string) string
func testBuildPath(t StorageType, path string) string {
if t == LocalStorageType {
return "/" + path
}
return path + "/"
}
func testSectionToPath(t StorageType, section string) string {
return testBuildPath(t, testSectionToBasePath[section])
}
func testSpecificPath(t StorageType, section string) string {
if t == LocalStorageType {
return "/specific_local_path"
}
return "specific_s3_base_path/"
}
func testDefaultDir(t StorageType) string {
if t == LocalStorageType {
return "default_local_path"
}
return "default_s3_base_path"
}
func testDefaultPath(t StorageType) string {
return testBuildPath(t, testDefaultDir(t))
}
func testSectionToDefaultPath(t StorageType, section string) string {
return testBuildPath(t, filepath.Join(testDefaultDir(t), testSectionToPath(t, section)))
}
func testLegacyPath(t StorageType, section string) string {
return testBuildPath(t, fmt.Sprintf("legacy_%s_path", section))
}
func testStorageTypeToSetting(t StorageType) string {
if t == LocalStorageType {
return "PATH"
}
return "MINIO_BASE_PATH"
}
var testSectionToLegacy = map[string]string{
"lfs": fmt.Sprintf(`
[server]
APP_DATA_PATH = /
LFS_CONTENT_PATH = %s
`, testLegacyPath(LocalStorageType, "lfs")),
"avatar": fmt.Sprintf(`
[picture]
AVATAR_UPLOAD_PATH = %s
`, testLegacyPath(LocalStorageType, "avatar")),
"repo-avatar": fmt.Sprintf(`
[picture]
REPOSITORY_AVATAR_UPLOAD_PATH = %s
`, testLegacyPath(LocalStorageType, "repo-avatar")),
}
func testStorageTypesDefaultAndSpecificStorage(t *testing.T, iniStr string) {
storageType := MinioStorageType
t.Run(string(storageType), func(t *testing.T) {
t.Run("override type minio", func(t *testing.T) {
storageSection := `
[storage]
STORAGE_TYPE = minio
`
testStorageTypesSpecificStorages(t, iniStr+storageSection, storageType, testSectionToPath, testSectionToPath)
})
})
storageType = LocalStorageType
t.Run(string(storageType), func(t *testing.T) {
storageSection := ""
testStorageTypesSpecificStorages(t, iniStr+storageSection, storageType, testSectionToPath, testSectionToPath)
t.Run("override type local", func(t *testing.T) {
storageSection := `
[storage]
STORAGE_TYPE = local
`
testStorageTypesSpecificStorages(t, iniStr+storageSection, storageType, testSectionToPath, testSectionToPath)
storageSection = fmt.Sprintf(`
[storage]
STORAGE_TYPE = local
PATH = %s
`, testDefaultPath(LocalStorageType))
testStorageTypesSpecificStorageSections(t, iniStr+storageSection, storageType, testSectionToDefaultPath, testSectionToPath)
})
})
}
func testStorageTypesSpecificStorageSections(t *testing.T, iniStr string, defaultStorageType StorageType, defaultStorageTypePath, testSectionToPath testSectionToPathFun) {
testSectionsMap := map[string]**Storage{
"attachment": &Attachment.Storage,
"lfs": &LFS.Storage,
"avatar": &Avatar.Storage,
"repo-avatar": &RepoAvatar.Storage,
"repo-archive": &RepoArchive.Storage,
"packages": &Packages.Storage,
// there are inconsistencies in how actions storage is determined in v1.20
// it is still alpha and undocumented and is ignored for now
//"storage.actions_log": &Actions.LogStorage,
//"actions.artifacts": &Actions.ArtifactStorage,
}
for sectionName, storage := range testSectionsMap {
t.Run(sectionName, func(t *testing.T) {
testStorageTypesSpecificStorage(t, iniStr, defaultStorageType, defaultStorageTypePath, testSectionToPath, sectionName, storage)
})
}
}
func testStorageTypesSpecificStorages(t *testing.T, iniStr string, defaultStorageType StorageType, defaultStorageTypePath, testSectionToPath testSectionToPathFun) {
testSectionsMap := map[string]**Storage{
"attachment": &Attachment.Storage,
"lfs": &LFS.Storage,
"avatar": &Avatar.Storage,
"repo-avatar": &RepoAvatar.Storage,
"repo-archive": &RepoArchive.Storage,
"packages": &Packages.Storage,
"storage.actions_log": &Actions.LogStorage,
"actions.artifacts": &Actions.ArtifactStorage,
}
for sectionName, storage := range testSectionsMap {
t.Run(sectionName, func(t *testing.T) {
if legacy, ok := testSectionToLegacy[sectionName]; ok {
if defaultStorageType == LocalStorageType {
t.Run("legacy local", func(t *testing.T) {
testStorageTypesSpecificStorage(t, iniStr+legacy, LocalStorageType, testLegacyPath, testSectionToPath, sectionName, storage)
testStorageTypesSpecificStorageTypeOverride(t, iniStr+legacy, LocalStorageType, testLegacyPath, testSectionToPath, sectionName, storage)
})
} else {
t.Run("legacy minio", func(t *testing.T) {
testStorageTypesSpecificStorage(t, iniStr+legacy, MinioStorageType, defaultStorageTypePath, testSectionToPath, sectionName, storage)
testStorageTypesSpecificStorageTypeOverride(t, iniStr+legacy, LocalStorageType, testLegacyPath, testSectionToPath, sectionName, storage)
})
}
}
for _, specificStorageType := range storageTypes {
testStorageTypesSpecificStorageTypeOverride(t, iniStr, specificStorageType, defaultStorageTypePath, testSectionToPath, sectionName, storage)
}
})
}
}
func testStorageTypesSpecificStorage(t *testing.T, iniStr string, defaultStorageType StorageType, defaultStorageTypePath, testSectionToPath testSectionToPathFun, sectionName string, storage **Storage) {
var section string
//
// Specific section is absent
//
testStoragePathMatch(t, iniStr, defaultStorageType, defaultStorageTypePath, sectionName, storage)
//
// Specific section is empty
//
section = fmt.Sprintf(`
[%s]
`,
sectionName)
testStoragePathMatch(t, iniStr+section, defaultStorageType, defaultStorageTypePath, sectionName, storage)
//
// Specific section with a path override
//
section = fmt.Sprintf(`
[%s]
%s = %s
`,
sectionName,
testStorageTypeToSetting(defaultStorageType),
testSpecificPath(defaultStorageType, ""))
testStoragePathMatch(t, iniStr+section, defaultStorageType, testSpecificPath, sectionName, storage)
}
func testStorageTypesSpecificStorageTypeOverride(t *testing.T, iniStr string, overrideStorageType StorageType, defaultStorageTypePath, testSectionToPath testSectionToPathFun, sectionName string, storage **Storage) {
var section string
t.Run("specific-"+string(overrideStorageType), func(t *testing.T) {
//
// Specific section with a path and storage type override
//
section = fmt.Sprintf(`
[%s]
STORAGE_TYPE = %s
%s = %s
`,
sectionName,
overrideStorageType,
testStorageTypeToSetting(overrideStorageType),
testSpecificPath(overrideStorageType, ""))
testStoragePathMatch(t, iniStr+section, overrideStorageType, testSpecificPath, sectionName, storage)
//
// Specific section with type override
//
section = fmt.Sprintf(`
[%s]
STORAGE_TYPE = %s
`,
sectionName,
overrideStorageType)
testStoragePathMatch(t, iniStr+section, overrideStorageType, defaultStorageTypePath, sectionName, storage)
})
}
func testStoragePathMatch(t *testing.T, iniStr string, storageType StorageType, testSectionToPath testSectionToPathFun, section string, storage **Storage) {
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err, iniStr)
assert.NoError(t, loadCommonSettingsFrom(cfg), iniStr)
assert.EqualValues(t, testSectionToPath(storageType, section), testStorageGetPath(*storage), iniStr)
assert.EqualValues(t, storageType, (*storage).Type, iniStr)
}

View file

@ -221,7 +221,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0) Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0)
sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https") sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https")
Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",") Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",")
schemes := make([]string, len(Service.ValidSiteURLSchemes)) schemes := make([]string, 0, len(Service.ValidSiteURLSchemes))
for _, scheme := range Service.ValidSiteURLSchemes { for _, scheme := range Service.ValidSiteURLSchemes {
scheme = strings.ToLower(strings.TrimSpace(scheme)) scheme = strings.ToLower(strings.TrimSpace(scheme))
if scheme != "" { if scheme != "" {

View file

@ -91,8 +91,8 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage,
} }
// Check to see if we already own this bucket // Check to see if we already own this bucket
exists, errBucketExists := minioClient.BucketExists(ctx, config.Bucket) exists, err := minioClient.BucketExists(ctx, config.Bucket)
if errBucketExists != nil { if err != nil {
return nil, convertMinioErr(err) return nil, convertMinioErr(err)
} }

View file

@ -19,6 +19,7 @@ var ErrInvalidReceiveHook = errors.New("Invalid JSON payload received over webho
type Hook struct { type Hook struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Type string `json:"type"` Type string `json:"type"`
BranchFilter string `json:"branch_filter"`
URL string `json:"-"` URL string `json:"-"`
Config map[string]string `json:"config"` Config map[string]string `json:"config"`
Events []string `json:"events"` Events []string `json:"events"`

View file

@ -10,6 +10,8 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/gobwas/glob"
) )
var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`) var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)
@ -48,6 +50,29 @@ func IsValidSiteURL(uri string) bool {
return false return false
} }
// IsEmailDomainListed checks whether the domain of an email address
// matches a list of domains
func IsEmailDomainListed(globs []glob.Glob, email string) bool {
if len(globs) == 0 {
return false
}
n := strings.LastIndex(email, "@")
if n <= 0 {
return false
}
domain := strings.ToLower(email[n+1:])
for _, g := range globs {
if g.Match(domain) {
return true
}
}
return false
}
// IsAPIURL checks if URL is current Gitea instance API URL // IsAPIURL checks if URL is current Gitea instance API URL
func IsAPIURL(uri string) bool { func IsAPIURL(uri string) bool {
return strings.HasPrefix(strings.ToLower(uri), strings.ToLower(setting.AppURL+"api")) return strings.HasPrefix(strings.ToLower(uri), strings.ToLower(setting.AppURL+"api"))

View file

@ -53,8 +53,12 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string {
secrets := map[string]string{} secrets := map[string]string{}
secrets["GITHUB_TOKEN"] = task.Token
secrets["GITEA_TOKEN"] = task.Token
if task.Job.Run.IsForkPullRequest { if task.Job.Run.IsForkPullRequest {
// ignore secrets for fork pull request // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated.
return secrets return secrets
} }
@ -78,13 +82,6 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s
} }
} }
if _, ok := secrets["GITHUB_TOKEN"]; !ok {
secrets["GITHUB_TOKEN"] = task.Token
}
if _, ok := secrets["GITEA_TOKEN"]; !ok {
secrets["GITEA_TOKEN"] = task.Token
}
return secrets return secrets
} }

View file

@ -326,11 +326,9 @@ func CreatePullRequest(ctx *context.APIContext) {
return return
} }
labelIDs = make([]int64, len(form.Labels)) labelIDs = make([]int64, 0, len(labels))
orgLabelIDs := make([]int64, len(form.Labels)) for _, label := range labels {
labelIDs = append(labelIDs, label.ID)
for i := range labels {
labelIDs[i] = labels[i].ID
} }
if ctx.Repo.Owner.IsOrganization() { if ctx.Repo.Owner.IsOrganization() {
@ -340,12 +338,12 @@ func CreatePullRequest(ctx *context.APIContext) {
return return
} }
for i := range orgLabels { orgLabelIDs := make([]int64, 0, len(orgLabels))
orgLabelIDs[i] = orgLabels[i].ID for _, orgLabel := range orgLabels {
orgLabelIDs = append(orgLabelIDs, orgLabel.ID)
} }
labelIDs = append(labelIDs, orgLabelIDs...)
} }
labelIDs = append(labelIDs, orgLabelIDs...)
} }
if form.Milestone > 0 { if form.Milestone > 0 {

View file

@ -28,23 +28,31 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
}() }()
var command *git.Command
if oldCommitID == git.EmptySHA {
// When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all":
// List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits
// So, it only lists the new commits received, doesn't list the commits already present in the receiving repository
command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all")
} else {
command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID)
}
// This is safe as force pushes are already forbidden // This is safe as force pushes are already forbidden
err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID). err = command.Run(&git.RunOpts{
Run(&git.RunOpts{ Env: env,
Env: env, Dir: repo.Path,
Dir: repo.Path, Stdout: stdoutWriter,
Stdout: stdoutWriter, PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { _ = stdoutWriter.Close()
_ = stdoutWriter.Close() err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) if err != nil {
if err != nil { log.Error("%v", err)
log.Error("%v", err) cancel()
cancel() }
} _ = stdoutReader.Close()
_ = stdoutReader.Close() return err
return err },
}, })
})
if err != nil && !isErrUnverifiedCommit(err) { if err != nil && !isErrUnverifiedCommit(err) {
log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
} }

View file

@ -0,0 +1,43 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package private
import (
"context"
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"github.com/stretchr/testify/assert"
)
var testReposDir = "tests/repos/"
func TestVerifyCommits(t *testing.T) {
unittest.PrepareTestEnv(t)
gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification")
defer gitRepo.Close()
assert.NoError(t, err)
testCases := []struct {
base, head string
verified bool
}{
{"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true},
{git.EmptySHA, "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit
{"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false},
{git.EmptySHA, "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit
}
for _, tc := range testCases {
err = verifyCommits(tc.base, tc.head, gitRepo, nil)
if tc.verified {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
}
}

View file

@ -0,0 +1,17 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package private
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
})
}

View file

@ -0,0 +1 @@
ref: refs/heads/main

View file

@ -0,0 +1,6 @@
[core]
repositoryformatversion = 0
filemode = false
bare = true
symlinks = false
ignorecase = true

View file

@ -0,0 +1 @@
d766f2917716d45be24bfa968b8409544941be32 refs/heads/main

View file

@ -0,0 +1 @@
0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea <gitea@fake.local> 1693148474 +0800 push

View file

@ -0,0 +1 @@
0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea <gitea@fake.local> 1693148474 +0800 push

View file

@ -0,0 +1,2 @@
x•ŽK
1Ù ÒéüAÄS¸ï$Í"32 ooð®ŠWð òÞ{!žæ`˜JC%¡.˜ $Ár]sѱe$ïmòâMƒ·)£÷±(O`ªbtlÐE[:;4–àHÐ1_û<5F>”rayýáþl“é÷~“ÊE­L@cå€Xv…Mþã":µMÛƒG«_}À?Ý

View file

@ -0,0 +1,2 @@
x<>1
!ES{ŠéAwGGa 9EúQg W·Èí#¹AªÞû©ÕZ§/£€³Œp±ì(¤(<28>ó®óBhÈÛ¼&áŸãÝ:pLY`ûÍãU†ð-µzŸÁ°ô†×ZM:<3A>†ü¡¨Êå€óxJ/ûG}:µ3

View file

@ -0,0 +1,3 @@
x•ŽA
Â0E]ç³$™L“ ˆx•L2µ]´<C2B4>
ÞÞê \}ø¼ÿøe[–¹:{êM°õZ5bŠ8$¡Äv ž°fÉRÍ37];Ôˆìbt¡Ò úå3‡$‰,tXœ¨G“÷>m ²”ªpýÅý1wÍ—²-7p<37>½£Ä„p¶ÉZs´Ç±®L̾¾´Íã¤åµLæëe@ó

View file

@ -0,0 +1,3 @@
xË®«FE3æ+zn%44æ!%Qxƒ<78>Û€s˜AÓ` 8Øæëã{£Ì2IM¶j•ª´¥Údèûf²Ìý2<C3BD>"‡$§e‰¶
-(â ­Ä!´ÝJ"åaŲ@•BaîùHo3 ŸVØòå<$<24>/)å$JøJDB¡•H¤§˜ü{¾ RRðûOù«nfšÿF†þOÀ‰
âq[°<>2„̇~ŒÍô¬Ô÷zjjðë<C3B0>ÒLÛÅÀ·}prm¬Fqhþä `@Ø«¦ªš®ª¥Õ˜fî?3Ç[7Š³…ê¨Ð) ^™þuÿÖ¿,µ<>Æl7©zÝÿr|&«Ou4<75>Ø9Ó:µÎQjôû·êÕ1x±õå6ÍQ‡÷ƒÀ%Áåtû‰sò¸íV‰| ( V¿<56>,aL,ù«G~²Ç<16>¹<C2B9>r¥ùûî@·`·Àþ$[! XËŠep©Œæ[8 oýä(« k£Z´Î³yóeйÙÆÄ«Y²¿kÖd€¯6•3¾;3ÜÔ RÔi ÞdYÓDk91V]/Cê#º¾&ÿêpo´Fáb¯¶}§¹ô¦òuW&]+m xaqdÜIõX¯þ3 Žƒ×ÆKÚÓI#Æi_ärgðñÁ<C3B1>ôôõÄ©7<C2A9>=ú`@[õŠ&AóṲ̂ÞLÖo3~MÆóõü8MGtö²ï>ÄôŒx¼vQ²(…<>aÅÄWŸo"¡Ës±r‰z”°eÓÅ­}å†QDñóÖ¨fK)ó˜mÆr>>•ª†¿‚†ÝÌšF8³x™ Ä×^J<> k{mczþI*²^ÆMb‡þ m¸6Š”M~h¹pÕÍ {¡¡±0€ö• ]€?nUwgþÉ <C2A0>ÿJ ³Ð±©Þ<ó7Û2

View file

@ -0,0 +1,3 @@
x•ŽA
Â0E]ç³$™L“ ˆx•L2µ]´<C2B4>
ÞÞê \}ø¼ÿøe[–¹:{êM°õZ5bŠ8$¡Äv ž°fÉRÍ37];Ôˆìbt¡Ò úå3‡$‰,tXœ¨G“÷>m ²”ªpýÅý1wÍ—²-7p<37>½£Ä„p¶ÉZs´Ç±®L̾¾´Íã¤åµLæëe@ó

View file

@ -0,0 +1 @@
d766f2917716d45be24bfa968b8409544941be32

View file

@ -0,0 +1,127 @@
# GPG key for abcde@gitea.com
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
4akc
=OYIo
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----
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
ZZtqph2TItCeV04HoaKHHc254akc
=PPG4
-----END PGP PRIVATE KEY BLOCK-----

View file

@ -459,7 +459,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
rootRepo.ID != ci.HeadRepo.ID && rootRepo.ID != ci.HeadRepo.ID &&
rootRepo.ID != baseRepo.ID { rootRepo.ID != baseRepo.ID {
canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode) canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
if canRead && rootRepo.AllowsPulls() { if canRead {
ctx.Data["RootRepo"] = rootRepo ctx.Data["RootRepo"] = rootRepo
if !fileOnly { if !fileOnly {
branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo)

View file

@ -514,6 +514,12 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
} }
func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
// Distinguish whether the owner of the repository
// is an individual or an organization
repoOwnerType := project_model.TypeIndividual
if repo.Owner.IsOrganization() {
repoOwnerType = project_model.TypeOrganization
}
var err error var err error
projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{
RepoID: repo.ID, RepoID: repo.ID,
@ -529,7 +535,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
OwnerID: repo.OwnerID, OwnerID: repo.OwnerID,
Page: -1, Page: -1,
IsClosed: util.OptionalBoolFalse, IsClosed: util.OptionalBoolFalse,
Type: project_model.TypeOrganization, Type: repoOwnerType,
}) })
if err != nil { if err != nil {
ctx.ServerError("GetProjects", err) ctx.ServerError("GetProjects", err)
@ -552,7 +558,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
OwnerID: repo.OwnerID, OwnerID: repo.OwnerID,
Page: -1, Page: -1,
IsClosed: util.OptionalBoolTrue, IsClosed: util.OptionalBoolTrue,
Type: project_model.TypeOrganization, Type: repoOwnerType,
}) })
if err != nil { if err != nil {
ctx.ServerError("GetProjects", err) ctx.ServerError("GetProjects", err)
@ -871,9 +877,17 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
// NewIssue render creating issue page // NewIssue render creating issue page
func NewIssue(ctx *context.Context) { func NewIssue(ctx *context.Context) {
issueConfig, _ := issue_service.GetTemplateConfigFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
hasTemplates := issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo)
if !issueConfig.BlankIssuesEnabled && hasTemplates {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if blank issues are disabled, just redirect to the "issues/choose" page with these parameters.
ctx.Redirect(fmt.Sprintf("%s/issues/new/choose?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
return
}
ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo) ctx.Data["NewIssueChooseTemplate"] = hasTemplates
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
title := ctx.FormString("title") title := ctx.FormString("title")
ctx.Data["TitleQuery"] = title ctx.Data["TitleQuery"] = title
@ -2834,53 +2848,55 @@ func NewComment(ctx *context.Context) {
// check whether the ref of PR <refs/pulls/pr_index/head> in base repo is consistent with the head commit of head branch in the head repo // check whether the ref of PR <refs/pulls/pr_index/head> in base repo is consistent with the head commit of head branch in the head repo
// get head commit of PR // get head commit of PR
prHeadRef := pull.GetGitRefName() if pull.Flow == issues_model.PullRequestFlowGithub {
if err := pull.LoadBaseRepo(ctx); err != nil { prHeadRef := pull.GetGitRefName()
ctx.ServerError("Unable to load base repo", err) if err := pull.LoadBaseRepo(ctx); err != nil {
return ctx.ServerError("Unable to load base repo", err)
}
prHeadCommitID, err := git.GetFullCommitID(ctx, pull.BaseRepo.RepoPath(), prHeadRef)
if err != nil {
ctx.ServerError("Get head commit Id of pr fail", err)
return
}
// get head commit of branch in the head repo
if err := pull.LoadHeadRepo(ctx); err != nil {
ctx.ServerError("Unable to load head repo", err)
return
}
if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
// todo localize
ctx.Flash.Error("The origin branch is delete, cannot reopen.")
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pull.Index))
return
}
headBranchRef := pull.GetGitHeadBranchRefName()
headBranchCommitID, err := git.GetFullCommitID(ctx, pull.HeadRepo.RepoPath(), headBranchRef)
if err != nil {
ctx.ServerError("Get head commit Id of head branch fail", err)
return
}
err = pull.LoadIssue(ctx)
if err != nil {
ctx.ServerError("load the issue of pull request error", err)
return
}
if prHeadCommitID != headBranchCommitID {
// force push to base repo
err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
Remote: pull.BaseRepo.RepoPath(),
Branch: pull.HeadBranch + ":" + prHeadRef,
Force: true,
Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
})
if err != nil {
ctx.ServerError("force push error", err)
return return
} }
prHeadCommitID, err := git.GetFullCommitID(ctx, pull.BaseRepo.RepoPath(), prHeadRef)
if err != nil {
ctx.ServerError("Get head commit Id of pr fail", err)
return
}
// get head commit of branch in the head repo
if err := pull.LoadHeadRepo(ctx); err != nil {
ctx.ServerError("Unable to load head repo", err)
return
}
if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
// todo localize
ctx.Flash.Error("The origin branch is delete, cannot reopen.")
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pull.Index))
return
}
headBranchRef := pull.GetGitHeadBranchRefName()
headBranchCommitID, err := git.GetFullCommitID(ctx, pull.HeadRepo.RepoPath(), headBranchRef)
if err != nil {
ctx.ServerError("Get head commit Id of head branch fail", err)
return
}
err = pull.LoadIssue(ctx)
if err != nil {
ctx.ServerError("load the issue of pull request error", err)
return
}
if prHeadCommitID != headBranchCommitID {
// force push to base repo
err := git.Push(ctx, pull.HeadRepo.RepoPath(), git.PushOptions{
Remote: pull.BaseRepo.RepoPath(),
Branch: pull.HeadBranch + ":" + prHeadRef,
Force: true,
Env: repo_module.InternalPushingEnvironment(pull.Issue.Poster, pull.BaseRepo),
})
if err != nil {
ctx.ServerError("force push error", err)
return
}
}
} }
} }

View file

@ -156,7 +156,7 @@ func Milestones(ctx *context.Context) {
} }
repoOpts := repo_model.SearchRepoOptions{ repoOpts := repo_model.SearchRepoOptions{
Actor: ctxUser, Actor: ctx.Doer,
OwnerID: ctxUser.ID, OwnerID: ctxUser.ID,
Private: true, Private: true,
AllPublic: false, // Include also all public repositories of users and public organisations AllPublic: false, // Include also all public repositories of users and public organisations
@ -437,7 +437,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// - Team has read permission to repository. // - Team has read permission to repository.
repoOpts := &repo_model.SearchRepoOptions{ repoOpts := &repo_model.SearchRepoOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
OwnerID: ctx.Doer.ID, OwnerID: ctxUser.ID,
Private: true, Private: true,
AllPublic: false, AllPublic: false,
AllLimited: false, AllLimited: false,

View file

@ -114,12 +114,13 @@ share the following fields:
* Example: (|(cn=gitea_users)(cn=admins)) * Example: (|(cn=gitea_users)(cn=admins))
* User Attribute in Group (optional) * User Attribute in Group (optional)
* Which user LDAP attribute is listed in the group. * The user attribute that is used to reference a user in the group object.
* Example: uid * Example: uid if the group objects contains a member: bender and the user object contains a uid: bender.
* Example: dn if the group object contains a member: uid=bender,ou=users,dc=planetexpress,dc=com.
* Group Attribute for User (optional) * Group Attribute for User (optional)
* Which group LDAP attribute contains an array above user attribute names. * The attribute of the group object that lists/contains the group members.
* Example: memberUid * Example: memberUid or member
* Team group map (optional) * Team group map (optional)
* Automatically add users to Organization teams, depending on LDAP group memberships. * Automatically add users to Organization teams, depending on LDAP group memberships.

View file

@ -13,10 +13,10 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
"gitea.com/go-chi/binding" "gitea.com/go-chi/binding"
"github.com/gobwas/glob"
) )
// InstallForm form for installation page // InstallForm form for installation page
@ -103,29 +103,6 @@ func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }
// IsEmailDomainListed checks whether the domain of an email address
// matches a list of domains
func IsEmailDomainListed(globs []glob.Glob, email string) bool {
if len(globs) == 0 {
return false
}
n := strings.LastIndex(email, "@")
if n <= 0 {
return false
}
domain := strings.ToLower(email[n+1:])
for _, g := range globs {
if g.Match(domain) {
return true
}
}
return false
}
// IsEmailDomainAllowed validates that the email address // IsEmailDomainAllowed validates that the email address
// provided by the user matches what has been configured . // provided by the user matches what has been configured .
// The email is marked as allowed if it matches any of the // The email is marked as allowed if it matches any of the
@ -133,10 +110,10 @@ func IsEmailDomainListed(globs []glob.Glob, email string) bool {
// domains in the blocklist, if any such list is not empty. // domains in the blocklist, if any such list is not empty.
func (f *RegisterForm) IsEmailDomainAllowed() bool { func (f *RegisterForm) IsEmailDomainAllowed() bool {
if len(setting.Service.EmailDomainAllowList) == 0 { if len(setting.Service.EmailDomainAllowList) == 0 {
return !IsEmailDomainListed(setting.Service.EmailDomainBlockList, f.Email) return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, f.Email)
} }
return IsEmailDomainListed(setting.Service.EmailDomainAllowList, f.Email) return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, f.Email)
} }
// MustChangePasswordForm form for updating your password after account creation // MustChangePasswordForm form for updating your password after account creation

View file

@ -334,9 +334,9 @@ func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *is
return err return err
} }
return db.WithTx(ctx, func(subCtx context.Context) error { return db.WithTx(ctx, func(ctx context.Context) error {
for _, review := range reviews { for _, review := range reviews {
if err := issues_model.DismissReview(subCtx, review, true); err != nil { if err := issues_model.DismissReview(ctx, review, true); err != nil {
return err return err
} }

View file

@ -186,6 +186,10 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
return fmt.Errorf("updateRepository: %w", err) return fmt.Errorf("updateRepository: %w", err)
} }
if err = repo_module.SyncReleasesWithTags(repo, gitRepo); err != nil {
return fmt.Errorf("SyncReleasesWithTags: %w", err)
}
return nil return nil
} }

View file

@ -114,12 +114,12 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
return nil, err return nil, err
} }
filelist := make([]string, len(filenames)) fileList := make([]string, 0, len(filenames))
for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) {
filelist = append(filelist, string(line)) fileList = append(fileList, string(line))
} }
return filelist, nil return fileList, nil
} }
// RemoveFilesFromIndex removes the given files from the index // RemoveFilesFromIndex removes the given files from the index

View file

@ -260,5 +260,6 @@ func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
AuthorizationHeader: authorizationHeader, AuthorizationHeader: authorizationHeader,
Updated: w.UpdatedUnix.AsTime(), Updated: w.UpdatedUnix.AsTime(),
Created: w.CreatedUnix.AsTime(), Created: w.CreatedUnix.AsTime(),
BranchFilter: w.BranchFilter,
}, nil }, nil
} }

View file

@ -19,15 +19,15 @@
<span class="inline required field"><label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label></span> <span class="inline required field"><label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label></span>
<div class="inline-grouped-list"> <div class="inline-grouped-list">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.public"}}</label> <label>{{.locale.Tr "org.settings.visibility.public"}}</label>
</div> </div>
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.limited"}}</label> <label>{{.locale.Tr "org.settings.visibility.limited"}}</label>
</div> </div>
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.private"}}</label> <label>{{.locale.Tr "org.settings.visibility.private"}}</label>
</div> </div>
</div> </div>

View file

@ -36,19 +36,19 @@
<label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label> <label for="visibility">{{.locale.Tr "org.settings.visibility"}}</label>
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.public"}}</label> <label>{{.locale.Tr "org.settings.visibility.public"}}</label>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.limited"}}</label> <label>{{.locale.Tr "org.settings.visibility.limited"}}</label>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}> <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}>
<label>{{.locale.Tr "org.settings.visibility.private"}}</label> <label>{{.locale.Tr "org.settings.visibility.private"}}</label>
</div> </div>
</div> </div>

View file

@ -86,10 +86,10 @@
</div> </div>
{{if and $.CanWriteProjects (ne .ID 0)}} {{if and $.CanWriteProjects (ne .ID 0)}}
<div class="ui dropdown jump item"> <div class="ui dropdown jump item">
<div class="not-mobile gt-px-3" tabindex="-1"> <div class="not-mobile gt-px-3">
{{svg "octicon-kebab-horizontal"}} {{svg "octicon-kebab-horizontal"}}
</div> </div>
<div class="menu user-menu" tabindex="-1"> <div class="menu user-menu">
<a class="item show-modal button" data-modal="#edit-project-board-modal-{{.ID}}"> <a class="item show-modal button" data-modal="#edit-project-board-modal-{{.ID}}">
{{svg "octicon-pencil"}} {{svg "octicon-pencil"}}
{{$.locale.Tr "repo.projects.column.edit"}} {{$.locale.Tr "repo.projects.column.edit"}}

View file

@ -77,33 +77,33 @@
<div class="inline field"> <div class="inline field">
<label>{{.locale.Tr "repo.template.items"}}</label> <label>{{.locale.Tr "repo.template.items"}}</label>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="git_content" type="checkbox" tabindex="0" {{if .git_content}}checked{{end}}> <input name="git_content" type="checkbox" {{if .git_content}}checked{{end}}>
<label>{{.locale.Tr "repo.template.git_content"}}</label> <label>{{.locale.Tr "repo.template.git_content"}}</label>
</div> </div>
<div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{.locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}> <div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{.locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}>
<input name="git_hooks" type="checkbox" tabindex="0" {{if .git_hooks}}checked{{end}}> <input name="git_hooks" type="checkbox" {{if .git_hooks}}checked{{end}}>
<label>{{.locale.Tr "repo.template.git_hooks"}}</label> <label>{{.locale.Tr "repo.template.git_hooks"}}</label>
</div> </div>
</div> </div>
<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="webhooks" type="checkbox" tabindex="0" {{if .webhooks}}checked{{end}}> <input name="webhooks" type="checkbox" {{if .webhooks}}checked{{end}}>
<label>{{.locale.Tr "repo.template.webhooks"}}</label> <label>{{.locale.Tr "repo.template.webhooks"}}</label>
</div> </div>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="topics" type="checkbox" tabindex="0" {{if .topics}}checked{{end}}> <input name="topics" type="checkbox" {{if .topics}}checked{{end}}>
<label>{{.locale.Tr "repo.template.topics"}}</label> <label>{{.locale.Tr "repo.template.topics"}}</label>
</div> </div>
</div> </div>
<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="avatar" type="checkbox" tabindex="0" {{if .avatar}}checked{{end}}> <input name="avatar" type="checkbox" {{if .avatar}}checked{{end}}>
<label>{{.locale.Tr "repo.template.avatar"}}</label> <label>{{.locale.Tr "repo.template.avatar"}}</label>
</div> </div>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="labels" type="checkbox" tabindex="0" {{if .labels}}checked{{end}}> <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
<label>{{.locale.Tr "repo.template.issue_labels"}}</label> <label>{{.locale.Tr "repo.template.issue_labels"}}</label>
</div> </div>
</div> </div>
@ -169,7 +169,7 @@
</div> </div>
<div class="inline field"> <div class="inline field">
<div class="ui checkbox" id="auto-init"> <div class="ui checkbox" id="auto-init">
<input name="auto_init" type="checkbox" tabindex="0" {{if .auto_init}}checked{{end}}> <input name="auto_init" type="checkbox" {{if .auto_init}}checked{{end}}>
<label>{{.locale.Tr "repo.auto_init"}}</label> <label>{{.locale.Tr "repo.auto_init"}}</label>
</div> </div>
</div> </div>
@ -204,7 +204,7 @@
<div class="inline field"> <div class="inline field">
<label>{{.locale.Tr "repo.template"}}</label> <label>{{.locale.Tr "repo.template"}}</label>
<div class="ui checkbox"> <div class="ui checkbox">
<input name="template" type="checkbox" tabindex="0"> <input name="template" type="checkbox">
<label>{{.locale.Tr "repo.template_helper"}}</label> <label>{{.locale.Tr "repo.template_helper"}}</label>
</div> </div>
</div> </div>

View file

@ -77,7 +77,7 @@
<div class="item" data-url="{{$.OwnForkRepo.Link}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{PathEscapeSegments $.HeadBranch}}">{{$OwnForkCompareName}}:{{.}}</div> <div class="item" data-url="{{$.OwnForkRepo.Link}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{PathEscapeSegments $.HeadBranch}}">{{$OwnForkCompareName}}:{{.}}</div>
{{end}} {{end}}
{{end}} {{end}}
{{if .RootRepo}} {{if and .RootRepo .RootRepo.AllowsPulls}}
{{range .RootRepoBranches}} {{range .RootRepoBranches}}
<div class="item" data-url="{{$.RootRepo.Link}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{PathEscapeSegments $.HeadBranch}}">{{$RootRepoCompareName}}:{{.}}</div> <div class="item" data-url="{{$.RootRepo.Link}}/compare/{{PathEscapeSegments .}}{{$.CompareSeparator}}{{PathEscape $.HeadUser.Name}}/{{PathEscape $.HeadRepo.Name}}:{{PathEscapeSegments $.HeadBranch}}">{{$RootRepoCompareName}}:{{.}}</div>
{{end}} {{end}}

View file

@ -16,7 +16,7 @@
</div> </div>
<div class="inline field"> <div class="inline field">
<div class="ui checkbox"> <div class="ui checkbox">
<input name="signoff" type="checkbox" tabindex="0"> <input name="signoff" type="checkbox">
<label>{{.locale.Tr "repo.editor.signoff_desc"}}</label> <label>{{.locale.Tr "repo.editor.signoff_desc"}}</label>
</div> </div>
</div> </div>

View file

@ -1,10 +1,9 @@
<div class="field"> <div class="field">
{{template "repo/issue/fields/header" .}} {{template "repo/issue/fields/header" .}}
{{$field := .}}
{{range $i, $opt := .item.Attributes.options}} {{range $i, $opt := .item.Attributes.options}}
<div class="field"> <div class="field">
<div class="ui checkbox"> <div class="ui checkbox">
<input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}> <input type="checkbox" name="form-field-{{$.item.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}>
<label>{{$opt.label}}</label> <label>{{$opt.label}}</label>
</div> </div>
</div> </div>

View file

@ -11,7 +11,7 @@
{{avatar $.Context .SignedUser 40}} {{avatar $.Context .SignedUser 40}}
<div class="ui segment content gt-my-0"> <div class="ui segment content gt-my-0">
<div class="field"> <div class="field">
<input name="title" id="issue_title" placeholder="{{.locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" tabindex="3" autofocus required maxlength="255" autocomplete="off"> <input name="title" id="issue_title" placeholder="{{.locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" autofocus required maxlength="255" autocomplete="off">
{{if .PageIsComparePull}} {{if .PageIsComparePull}}
<div class="title_wip_desc" data-wip-prefixes="{{JsonUtils.EncodeToString .PullRequestWorkInProgressPrefixes}}">{{.locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> <div class="title_wip_desc" data-wip-prefixes="{{JsonUtils.EncodeToString .PullRequestWorkInProgressPrefixes}}">{{.locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div>
{{end}} {{end}}
@ -35,7 +35,7 @@
{{template "repo/issue/comment_tab" .}} {{template "repo/issue/comment_tab" .}}
{{end}} {{end}}
<div class="text right"> <div class="text right">
<button class="ui green button loading-button" tabindex="6"> <button class="ui green button loading-button">
{{if .PageIsComparePull}} {{if .PageIsComparePull}}
{{.locale.Tr "repo.pulls.create"}} {{.locale.Tr "repo.pulls.create"}}
{{else}} {{else}}

View file

@ -609,7 +609,7 @@
</div> </div>
<div class="field"> <div class="field">
<div class="ui fluid dropdown selection" tabindex="0"> <div class="ui fluid dropdown selection">
<select name="reason"> <select name="reason">
<option value=""> </option> <option value=""> </option>

View file

@ -90,10 +90,10 @@
</div> </div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived) (ne .ID 0)}} {{if and $.CanWriteProjects (not $.Repository.IsArchived) (ne .ID 0)}}
<div class="ui dropdown jump item"> <div class="ui dropdown jump item">
<div class="not-mobile gt-px-3" tabindex="-1"> <div class="not-mobile gt-px-3">
{{svg "octicon-kebab-horizontal"}} {{svg "octicon-kebab-horizontal"}}
</div> </div>
<div class="menu user-menu" tabindex="-1"> <div class="menu user-menu">
<a class="item show-modal button" data-modal="#edit-project-board-modal-{{.ID}}"> <a class="item show-modal button" data-modal="#edit-project-board-modal-{{.ID}}">
{{svg "octicon-pencil"}} {{svg "octicon-pencil"}}
{{$.locale.Tr "repo.projects.column.edit"}} {{$.locale.Tr "repo.projects.column.edit"}}

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