mirror of
https://github.com/go-gitea/gitea
synced 2026-02-08 15:58:10 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acdcfcc6eb | ||
|
|
727b1914b4 | ||
|
|
60181eb599 | ||
|
|
a0ca311165 | ||
|
|
430fe6c0c1 | ||
|
|
b6379d2f16 | ||
|
|
928c0d4f46 | ||
|
|
222d16e6ea | ||
|
|
09df5c9c7d | ||
|
|
fc4e08f804 | ||
|
|
68bd1dd89d | ||
|
|
55990ebf92 | ||
|
|
245e8d10c2 | ||
|
|
529604a044 | ||
|
|
6cfe67cfc3 | ||
|
|
9149221845 | ||
|
|
6e3aaa9975 | ||
|
|
3f6ddd9bee | ||
|
|
65d96725bb | ||
|
|
4588c7b705 | ||
|
|
47de6e3b54 | ||
|
|
5123ed3191 | ||
|
|
9f2a1a55e6 | ||
|
|
935bfe6445 | ||
|
|
2ac78c75d0 | ||
|
|
b1dae9f2c8 | ||
|
|
7bffb923ce | ||
|
|
0c10c3a282 | ||
|
|
09a5067c0c | ||
|
|
a0f89ba8c7 | ||
|
|
6d47b63be2 | ||
|
|
8dccea02f2 | ||
|
|
03f29db46d |
+41
-4
@@ -4,7 +4,44 @@ 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
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.21.10](https://github.com/go-gitea/gitea/releases/tag/1.21.10) - 2024-03-25
|
||||
## [1.21.11](https://github.com/go-gitea/gitea/releases/tag/v1.21.11) - 2024-04-07
|
||||
|
||||
* SECURITY
|
||||
* Use go1.21.9 to include Golang security fix
|
||||
* Fix possible renderer security problem (#30136) (#30315)
|
||||
* BUGFIXES
|
||||
* Fix close file in the Upload func (#30262) (#30269)
|
||||
* Fix inline math blocks can't be preceeded/followed by alphanumerical characters (#30175) (#30250)
|
||||
* Fix missing 0 prefix of GPG key id (#30245) (#30247)
|
||||
* Include encoding in signature payload (#30174) (#30181)
|
||||
* Move from `max( id )` to `max( index )` for latest commit statuses (#30076) (#30155)
|
||||
* Load attachments for code comments (#30124) (#30126)
|
||||
* Fix gitea doctor will remove repo-avatar files when executing command storage-archives (#30094) (#30120)
|
||||
* Fix possible data race on tests (#30093) (#30108)
|
||||
* Performance optimization for git push (#30104)
|
||||
* Fix duplicate migrated milestones (#30102) (#30105)
|
||||
* Fix panic for fixBrokenRepoUnits16961 (#30068) (#30100)
|
||||
* Fix incorrect SVGs (#30087)
|
||||
* Fix create commit status (#30225) (#30340)
|
||||
* Performance optimization for git push (#30104) (#30354)
|
||||
* Fix misuse of unsupported global variables (#30402)
|
||||
* Fix to delete the cookie when AppSubURL is non-empty (#30375) (#30468)
|
||||
* Avoid user does not exist error when detecting schedule actions when the commit author is an external user (#30357) (#30408)
|
||||
* Change the default maxPerPage for gitbucket (#30392) (#30471)
|
||||
* Check the token's owner and repository when registering a runner (#30406) (#30412)
|
||||
* Avoid losing token when updating mirror settings (#30429) (#30466)
|
||||
* Fix commit status cache which missed target_url (#30426) (#30445)
|
||||
* Fix rename branch 500 when the target branch is deleted but exist in database (#30430) (#30437)
|
||||
* Fix mirror error when mirror repo is empty (#30432) (#30467)
|
||||
* Use db.ListOptions directly instead of Paginator interface to make it easier to use and fix performance of /pulls and /issues (#29990) (#30447)
|
||||
* Fix code owners will not be mentioned when a pull request comes from a forked repository (#30476) (#30497)
|
||||
* DOCS
|
||||
* Update actions variables documents (#30394) (#30405)
|
||||
* MISC
|
||||
* Update katex to 0.16.10 (#30089)
|
||||
* Upgrade go-sqlite to v1.14.22 (#30462)
|
||||
|
||||
## [1.21.10](https://github.com/go-gitea/gitea/releases/tag/v1.21.10) - 2024-03-25
|
||||
|
||||
* BUGFIXES
|
||||
* Fix Add/Remove WIP on pull request title failure (#29999) (#30066)
|
||||
@@ -14,7 +51,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
* Remove duplicate option in admin screen and now-unused translation keys (#28492) (#30024)
|
||||
* Fix manual merge form and 404 page templates (#30000)
|
||||
|
||||
## [1.21.9](https://github.com/go-gitea/gitea/releases/tag/1.21.9) - 2024-03-21
|
||||
## [1.21.9](https://github.com/go-gitea/gitea/releases/tag/v1.21.9) - 2024-03-21
|
||||
|
||||
* PERFORMANCE
|
||||
* Only do counting when count_only=true for repo dashboard (#29884) (#29905)
|
||||
@@ -47,7 +84,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
* Performance improvements for pull request list page (#29900) (#29972)
|
||||
* Fix bugs in rerunning jobs (#29983) (#29955)
|
||||
|
||||
## [1.21.8](https://github.com/go-gitea/gitea/releases/tag/1.21.8) - 2024-03-12
|
||||
## [1.21.8](https://github.com/go-gitea/gitea/releases/tag/v1.21.8) - 2024-03-12
|
||||
|
||||
* SECURITY
|
||||
* Only use supported sort orders for "/explore/users" page (#29430) (#29443)
|
||||
@@ -92,7 +129,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
* Fixing the issue when status checks per rule matches multiple actions (#29631) (#29655)
|
||||
* Improve contrast on blame timestamp, fix double border (#29482) (#29485)
|
||||
|
||||
## [1.21.7](https://github.com/go-gitea/gitea/releases/tag/1.21.7) - 2024-02-26
|
||||
## [1.21.7](https://github.com/go-gitea/gitea/releases/tag/v1.21.7) - 2024-02-26
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Users with `read` permission of pull requests can be assigned too (#27263) (#29372)
|
||||
|
||||
+23
-14
@@ -446,23 +446,26 @@ Gitea or set your environment appropriately.`, "")
|
||||
|
||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||
for _, res := range results {
|
||||
if !res.Message {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if res.Create {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
os.Stderr.Sync()
|
||||
hookPrintResult(res.Message, res.Create, res.Branch, res.URL)
|
||||
}
|
||||
}
|
||||
|
||||
func hookPrintResult(output, isCreate bool, branch, url string) {
|
||||
if !output {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if isCreate {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
os.Stderr.Sync()
|
||||
}
|
||||
|
||||
func pushOptions() map[string]string {
|
||||
opts := make(map[string]string)
|
||||
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
|
||||
@@ -688,6 +691,12 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
err = writeFlushPktLine(ctx, os.Stdout)
|
||||
|
||||
if err == nil {
|
||||
for _, res := range resp.Results {
|
||||
hookPrintResult(res.ShouldShowMessage, res.IsCreatePR, res.HeadBranch, res.URL)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ In this option, the idea is that the host SSH uses an `AuthorizedKeysCommand` in
|
||||
```bash
|
||||
cat <<"EOF" | sudo tee /home/git/docker-shell
|
||||
#!/bin/sh
|
||||
/usr/bin/docker exec -i --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
|
||||
/usr/bin/docker exec -i -u git --env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" gitea sh "$@"
|
||||
EOF
|
||||
sudo chmod +x /home/git/docker-shell
|
||||
sudo usermod -s /home/git/docker-shell git
|
||||
@@ -560,7 +560,7 @@ Add the following block to `/etc/ssh/sshd_config`, on the host:
|
||||
```bash
|
||||
Match User git
|
||||
AuthorizedKeysCommandUser git
|
||||
AuthorizedKeysCommand /usr/bin/docker exec -i gitea /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k
|
||||
AuthorizedKeysCommand /usr/bin/docker exec -i -u git gitea /usr/local/bin/gitea keys -c /data/gitea/conf/app.ini -e git -u %u -t %t -k %k
|
||||
```
|
||||
|
||||
(From 1.16.0 you will not need to set the `-c /data/gitea/conf/app.ini` option.)
|
||||
|
||||
@@ -301,34 +301,3 @@ sudo systemctl enable act_runner --now
|
||||
```
|
||||
|
||||
If using Docker, the `act_runner` user should also be added to the `docker` group before starting the service. Keep in mind that this effectively gives `act_runner` root access to the system [[1]](https://docs.docker.com/engine/security/#docker-daemon-attack-surface).
|
||||
|
||||
## Configuration variable
|
||||
|
||||
You can create configuration variables on the user, organization and repository level.
|
||||
The level of the variable depends on where you created it.
|
||||
|
||||
### Naming conventions
|
||||
|
||||
The following rules apply to variable names:
|
||||
|
||||
- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
|
||||
|
||||
- Variable names must not start with the `GITHUB_` and `GITEA_` prefix.
|
||||
|
||||
- Variable names must not start with a number.
|
||||
|
||||
- Variable names are case-insensitive.
|
||||
|
||||
- Variable names must be unique at the level they are created at.
|
||||
|
||||
- Variable names must not be `CI`.
|
||||
|
||||
### Using variable
|
||||
|
||||
After creating configuration variables, they will be automatically filled in the `vars` context.
|
||||
They can be accessed through expressions like `{{ vars.VARIABLE_NAME }}` in the workflow.
|
||||
|
||||
### Precedence
|
||||
|
||||
If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence:
|
||||
A repository variable will always be chosen over an organization/user variable.
|
||||
|
||||
@@ -258,32 +258,3 @@ Runner的标签用于确定Runner可以运行哪些Job以及如何运行它们
|
||||
Runner将从Gitea实例获取Job并自动运行它们。
|
||||
|
||||
由于Act Runner仍处于开发中,建议定期检查最新版本并进行升级。
|
||||
|
||||
## 变量
|
||||
|
||||
您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。
|
||||
|
||||
### 命名规则
|
||||
|
||||
以下规则适用于变量名:
|
||||
|
||||
- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。
|
||||
|
||||
- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。
|
||||
|
||||
- 变量名称不能以数字开头。
|
||||
|
||||
- 变量名称不区分大小写。
|
||||
|
||||
- 变量名称在创建它们的级别上必须是唯一的。
|
||||
|
||||
- 变量名称不能为 “CI”。
|
||||
|
||||
### 使用
|
||||
|
||||
创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `{{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。
|
||||
|
||||
### 优先级
|
||||
|
||||
如果同名变量存在于多个级别,则级别最低的变量优先。
|
||||
仓库级别的变量总是比组织或者用户级别的变量优先被选中。
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
date: "2024-04-10T22:21:00+08:00"
|
||||
title: "Variables"
|
||||
slug: "actions-variables"
|
||||
sidebar_position: 25
|
||||
draft: false
|
||||
toc: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "actions"
|
||||
name: "Variables"
|
||||
sidebar_position: 25
|
||||
identifier: "actions-variables"
|
||||
---
|
||||
|
||||
## Variables
|
||||
|
||||
You can create configuration variables on the user, organization and repository level.
|
||||
The level of the variable depends on where you created it. When creating a variable, the
|
||||
key will be converted to uppercase. You need use uppercase on the yaml file.
|
||||
|
||||
### Naming conventions
|
||||
|
||||
The following rules apply to variable names:
|
||||
|
||||
- Variable names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
|
||||
- Variable names must not start with the `GITHUB_` and `GITEA_` prefix.
|
||||
- Variable names must not start with a number.
|
||||
- Variable names are case-insensitive.
|
||||
- Variable names must be unique at the level they are created at.
|
||||
- Variable names must not be `CI`.
|
||||
|
||||
### Using variable
|
||||
|
||||
After creating configuration variables, they will be automatically filled in the `vars` context.
|
||||
They can be accessed through expressions like `${{ vars.VARIABLE_NAME }}` in the workflow.
|
||||
|
||||
### Precedence
|
||||
|
||||
If a variable with the same name exists at multiple levels, the variable at the lowest level takes precedence:
|
||||
A repository variable will always be chosen over an organization/user variable.
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
date: "2024-04-10T22:21:00+08:00"
|
||||
title: "变量"
|
||||
slug: "actions-variables"
|
||||
sidebar_position: 25
|
||||
draft: false
|
||||
toc: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "actions"
|
||||
name: "变量"
|
||||
sidebar_position: 25
|
||||
identifier: "actions-variables"
|
||||
---
|
||||
|
||||
## 变量
|
||||
|
||||
您可以创建用户、组织和仓库级别的变量。变量的级别取决于创建它的位置。当创建变量时,变量的名称会被
|
||||
转换为大写,在yaml文件中引用时需要使用大写。
|
||||
|
||||
### 命名规则
|
||||
|
||||
以下规则适用于变量名:
|
||||
|
||||
- 变量名称只能包含字母数字字符 (`[a-z]`, `[A-Z]`, `[0-9]`) 或下划线 (`_`)。不允许使用空格。
|
||||
- 变量名称不能以 `GITHUB_` 和 `GITEA_` 前缀开头。
|
||||
- 变量名称不能以数字开头。
|
||||
- 变量名称不区分大小写。
|
||||
- 变量名称在创建它们的级别上必须是唯一的。
|
||||
- 变量名称不能为 `CI`。
|
||||
|
||||
### 使用
|
||||
|
||||
创建配置变量后,它们将自动填充到 `vars` 上下文中。您可以在工作流中使用类似 `${{ vars.VARIABLE_NAME }}` 这样的表达式来使用它们。
|
||||
|
||||
### 优先级
|
||||
|
||||
如果同名变量存在于多个级别,则级别最低的变量优先。
|
||||
仓库级别的变量总是比组织或者用户级别的变量优先被选中。
|
||||
@@ -73,7 +73,7 @@ require (
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.78.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/meilisearch/meilisearch-go v0.25.1
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
|
||||
@@ -716,8 +716,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/meilisearch/meilisearch-go v0.25.1 h1:D5wY22sn5kkpRH3uYMGlwltdUEq5regIFmO7awHz3Vo=
|
||||
|
||||
@@ -44,6 +44,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error {
|
||||
schedule.TriggerUser = user_model.NewActionsUser()
|
||||
} else {
|
||||
schedule.TriggerUser = users[schedule.TriggerUserID]
|
||||
if schedule.TriggerUser == nil {
|
||||
schedule.TriggerUser = user_model.NewGhostUser()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -100,13 +100,6 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error)
|
||||
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
||||
variables := map[string]string{}
|
||||
|
||||
// Global
|
||||
globalVariables, err := FindVariables(ctx, FindVariablesOpts{})
|
||||
if err != nil {
|
||||
log.Error("find global variables: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Org / User level
|
||||
ownerVariables, err := FindVariables(ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID})
|
||||
if err != nil {
|
||||
@@ -121,8 +114,8 @@ func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Level precedence: Repo > Org / User > Global
|
||||
for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) {
|
||||
// Level precedence: Repo > Org / User
|
||||
for _, v := range append(ownerVariables, repoVariables...) {
|
||||
variables[v.Name] = v.Data
|
||||
}
|
||||
|
||||
|
||||
@@ -139,13 +139,7 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
|
||||
}
|
||||
}
|
||||
|
||||
keyID := ""
|
||||
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
||||
keyID = fmt.Sprintf("%X", *sig.IssuerKeyId)
|
||||
}
|
||||
if keyID == "" && sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
|
||||
keyID = fmt.Sprintf("%X", sig.IssuerFingerprint[12:20])
|
||||
}
|
||||
keyID := tryGetKeyIDFromSignature(sig)
|
||||
defaultReason := NoKeyFound
|
||||
|
||||
// First check if the sig has a keyID and if so just look at that
|
||||
|
||||
@@ -134,3 +134,13 @@ func extractSignature(s string) (*packet.Signature, error) {
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func tryGetKeyIDFromSignature(sig *packet.Signature) string {
|
||||
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
||||
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
|
||||
}
|
||||
if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
|
||||
return fmt.Sprintf("%016X", sig.IssuerFingerprint[12:20])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -391,3 +393,13 @@ epiDVQ==
|
||||
assert.Equal(t, time.Unix(1586105389, 0), expire)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryGetKeyIDFromSignature(t *testing.T) {
|
||||
assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{}))
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
|
||||
IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
|
||||
}))
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
|
||||
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
|
||||
}))
|
||||
}
|
||||
|
||||
+25
-6
@@ -292,6 +292,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
// check whether from branch exist
|
||||
var branch Branch
|
||||
exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
|
||||
if err != nil {
|
||||
@@ -303,6 +304,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
}
|
||||
}
|
||||
|
||||
// check whether to branch exist or is_deleted
|
||||
var dstBranch Branch
|
||||
exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exist {
|
||||
if !dstBranch.IsDeleted {
|
||||
return ErrBranchAlreadyExists{
|
||||
BranchName: to,
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 1. update branch in database
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||
Name: to,
|
||||
@@ -357,12 +376,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. do git action
|
||||
if err = gitAction(ctx, isDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. insert renamed branch record
|
||||
// 5. insert renamed branch record
|
||||
renamedBranch := &RenamedBranch{
|
||||
RepoID: repo.ID,
|
||||
From: from,
|
||||
@@ -373,6 +387,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. do git action
|
||||
if err = gitAction(ctx, isDefault); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
|
||||
+73
-47
@@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommitStatus holds a single Status of a single Commit
|
||||
@@ -281,44 +282,48 @@ type CommitStatusIndex struct {
|
||||
|
||||
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
|
||||
func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, int64, error) {
|
||||
ids := make([]int64, 0, 10)
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha).
|
||||
Select("max( id ) as id").
|
||||
GroupBy("context_hash").OrderBy("max( id ) desc")
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha)
|
||||
}
|
||||
indices := make([]int64, 0, 10)
|
||||
sess := getBase().Select("max( `index` ) as `index`").
|
||||
GroupBy("context_hash").OrderBy("max( `index` ) desc")
|
||||
if !listOptions.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
count, err := sess.FindAndCount(&ids)
|
||||
count, err := sess.FindAndCount(&indices)
|
||||
if err != nil {
|
||||
return nil, count, err
|
||||
}
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) == 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(indices))
|
||||
if len(indices) == 0 {
|
||||
return statuses, count, nil
|
||||
}
|
||||
return statuses, count, db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
return statuses, count, getBase().And(builder.In("`index`", indices)).Find(&statuses)
|
||||
}
|
||||
|
||||
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
|
||||
func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) {
|
||||
type result struct {
|
||||
ID int64
|
||||
Index int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
results := make([]result, 0, len(repoIDsToLatestCommitSHAs))
|
||||
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
}
|
||||
|
||||
// Create a disjunction of conditions for each repoID and SHA pair
|
||||
conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs))
|
||||
for repoID, sha := range repoIDsToLatestCommitSHAs {
|
||||
conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha})
|
||||
}
|
||||
sess = sess.Where(builder.Or(conds...)).
|
||||
Select("max( id ) as id, repo_id").
|
||||
GroupBy("context_hash, repo_id").OrderBy("max( id ) desc")
|
||||
sess := getBase().Where(builder.Or(conds...)).
|
||||
Select("max( `index` ) as `index`, repo_id").
|
||||
GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc")
|
||||
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
|
||||
@@ -327,15 +332,21 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, len(results))
|
||||
repoStatuses := make(map[int64][]*CommitStatus)
|
||||
for _, result := range results {
|
||||
ids = append(ids, result.ID)
|
||||
}
|
||||
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) > 0 {
|
||||
err = db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
if len(results) > 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(results))
|
||||
|
||||
conds = make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
cond := builder.Eq{
|
||||
"`index`": result.Index,
|
||||
"repo_id": result.RepoID,
|
||||
"sha": repoIDsToLatestCommitSHAs[result.RepoID],
|
||||
}
|
||||
conds = append(conds, cond)
|
||||
}
|
||||
err = getBase().Where(builder.Or(conds...)).Find(&statuses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -352,42 +363,43 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
|
||||
// GetLatestCommitStatusForRepoCommitIDs returns all statuses with a unique context for a given list of repo-sha pairs
|
||||
func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, commitIDs []string) (map[string][]*CommitStatus, error) {
|
||||
type result struct {
|
||||
ID int64
|
||||
Sha string
|
||||
Index int64
|
||||
SHA string
|
||||
}
|
||||
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
|
||||
}
|
||||
results := make([]result, 0, len(commitIDs))
|
||||
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
|
||||
// Create a disjunction of conditions for each repoID and SHA pair
|
||||
conds := make([]builder.Cond, 0, len(commitIDs))
|
||||
for _, sha := range commitIDs {
|
||||
conds = append(conds, builder.Eq{"sha": sha})
|
||||
}
|
||||
sess = sess.Where(builder.Eq{"repo_id": repoID}.And(builder.Or(conds...))).
|
||||
Select("max( id ) as id, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( id ) desc")
|
||||
sess := getBase().And(builder.Or(conds...)).
|
||||
Select("max( `index` ) as `index`, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
|
||||
|
||||
err := sess.Find(&results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, len(results))
|
||||
repoStatuses := make(map[string][]*CommitStatus)
|
||||
for _, result := range results {
|
||||
ids = append(ids, result.ID)
|
||||
}
|
||||
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) > 0 {
|
||||
err = db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
if len(results) > 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(results))
|
||||
|
||||
conds = make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
|
||||
}
|
||||
err = getBase().And(builder.Or(conds...)).Find(&statuses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Group the statuses by repo ID
|
||||
// Group the statuses by commit
|
||||
for _, status := range statuses {
|
||||
repoStatuses[status.SHA] = append(repoStatuses[status.SHA], status)
|
||||
}
|
||||
@@ -398,22 +410,36 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co
|
||||
|
||||
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
|
||||
func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) {
|
||||
type result struct {
|
||||
Index int64
|
||||
SHA string
|
||||
}
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
|
||||
}
|
||||
|
||||
start := timeutil.TimeStampNow().AddDuration(-before)
|
||||
ids := make([]int64, 0, 10)
|
||||
if err := db.GetEngine(ctx).Table("commit_status").
|
||||
Where("repo_id = ?", repoID).
|
||||
And("updated_unix >= ?", start).
|
||||
Select("max( id ) as id").
|
||||
GroupBy("context_hash").OrderBy("max( id ) desc").
|
||||
Find(&ids); err != nil {
|
||||
results := make([]result, 0, 10)
|
||||
|
||||
sess := getBase().And("updated_unix >= ?", start).
|
||||
Select("max( `index` ) as `index`, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
|
||||
|
||||
err := sess.Find(&results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contexts := make([]string, 0, len(ids))
|
||||
if len(ids) == 0 {
|
||||
contexts := make([]string, 0, len(results))
|
||||
if len(results) == 0 {
|
||||
return contexts, nil
|
||||
}
|
||||
return contexts, db.GetEngine(ctx).Select("context").Table("commit_status").In("id", ids).Find(&contexts)
|
||||
|
||||
conds := make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
|
||||
}
|
||||
return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts)
|
||||
}
|
||||
|
||||
// NewCommitStatusOptions holds options for creating a CommitStatus
|
||||
|
||||
@@ -74,6 +74,10 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := comments.LoadAttachments(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find all reviews by ReviewID
|
||||
reviews := make(map[int64]*Review)
|
||||
ids := make([]int64, 0, len(comments))
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
// IssuesOptions represents options of an issue.
|
||||
type IssuesOptions struct { //nolint
|
||||
db.Paginator
|
||||
Paginator *db.ListOptions
|
||||
RepoIDs []int64 // overwrites RepoCond if the length is not 0
|
||||
RepoCond builder.Cond
|
||||
AssigneeID int64
|
||||
@@ -103,23 +103,11 @@ func applyLimit(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
|
||||
return sess
|
||||
}
|
||||
|
||||
// Warning: Do not use GetSkipTake() for *db.ListOptions
|
||||
// Its implementation could reset the page size with setting.API.MaxResponseItems
|
||||
if listOptions, ok := opts.Paginator.(*db.ListOptions); ok {
|
||||
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
|
||||
var start int
|
||||
if listOptions.Page == 0 {
|
||||
start = 0
|
||||
} else {
|
||||
start = (listOptions.Page - 1) * listOptions.PageSize
|
||||
}
|
||||
sess.Limit(listOptions.PageSize, start)
|
||||
}
|
||||
return sess
|
||||
start := 0
|
||||
if opts.Paginator.Page > 1 {
|
||||
start = (opts.Paginator.Page - 1) * opts.Paginator.PageSize
|
||||
}
|
||||
|
||||
start, limit := opts.Paginator.GetSkipTake()
|
||||
sess.Limit(limit, start)
|
||||
sess.Limit(opts.Paginator.PageSize, start)
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
@@ -69,13 +69,17 @@ func CountIssuesByRepo(ctx context.Context, opts *IssuesOptions) (map[int64]int6
|
||||
}
|
||||
|
||||
// CountIssues number return of issues by given conditions.
|
||||
func CountIssues(ctx context.Context, opts *IssuesOptions) (int64, error) {
|
||||
func CountIssues(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Cond) (int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Select("COUNT(issue.id) AS count").
|
||||
Table("issue").
|
||||
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
applyConditions(sess, opts)
|
||||
|
||||
for _, cond := range otherConds {
|
||||
sess.And(cond)
|
||||
}
|
||||
|
||||
return sess.Count()
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@@ -401,6 +402,8 @@ func DeleteOrganization(ctx context.Context, org *Organization) error {
|
||||
&TeamUnit{OrgID: org.ID},
|
||||
&TeamInvite{OrgID: org.ID},
|
||||
&secret_model.Secret{OwnerID: org.ID},
|
||||
&actions_model.ActionRunner{OwnerID: org.ID},
|
||||
&actions_model.ActionRunnerToken{OwnerID: org.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("DeleteBeans: %w", err)
|
||||
}
|
||||
|
||||
@@ -216,6 +216,12 @@ func fixBrokenRepoUnit16961(repoUnit *repo_model.RepoUnit, bs []byte) (fixed boo
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var cfg any
|
||||
err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
switch repoUnit.Type {
|
||||
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
|
||||
cfg := &repo_model.UnitConfig{}
|
||||
|
||||
@@ -162,7 +162,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
|
||||
if opts.RepoArchives || opts.All {
|
||||
if err := commonCheckStorage(ctx, logger, autofix,
|
||||
&commonStorageCheckOptions{
|
||||
storer: storage.RepoAvatars,
|
||||
storer: storage.RepoArchives,
|
||||
isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
|
||||
exists, err := repo.ExistsRepoArchiverWithStoragePath(ctx, path)
|
||||
if err == nil || errors.Is(err, util.ErrInvalidArgument) {
|
||||
|
||||
@@ -47,6 +47,12 @@ func convertPGPSignature(c *object.Commit) *CommitGPGSignature {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.Encoding != "" && c.Encoding != "UTF-8" {
|
||||
if _, err = fmt.Fprintf(&w, "\nencoding %s\n", c.Encoding); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = fmt.Fprintf(&w, "\n\n%s", c.Message); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ readLoop:
|
||||
commit.Committer = &Signature{}
|
||||
commit.Committer.Decode(data)
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "encoding":
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "gpgsig":
|
||||
_, _ = signatureSB.Write(data)
|
||||
_ = signatureSB.WriteByte('\n')
|
||||
|
||||
@@ -125,6 +125,73 @@ empty commit`, commitFromReader.Signature.Payload)
|
||||
assert.EqualValues(t, commitFromReader, commitFromReader2)
|
||||
}
|
||||
|
||||
func TestCommitWithEncodingFromReader(t *testing.T) {
|
||||
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
|
||||
tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
|
||||
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
|
||||
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
encoding ISO-8859-1
|
||||
gpgsig -----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow
|
||||
Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR
|
||||
gizKa2COiGtugv8fE+TKqXKaJx6uJUJEjaBd8E9Af9PrAzjWj+A84lU6/PgPS8hq
|
||||
zOfZraLOEWRH4tZcS+u2yFLu3ez2Wqh1xW5LNy7xqEedMXEFD1HwSJ0+pjacNkzr
|
||||
frp6Asyt7xRI6YmgFJZJoRsS3Ktr6rtKeRL2IErSQQyorOqj6gKrglhrhfG/114j
|
||||
FKB1v4or0WZ1DE8iP2SJZ3n+/K1IuWAINh7MVdb7PndfBPEa+IL+ucNk5uzEE8Jd
|
||||
G8smGxXUeFEt2cP1dj2W8EgAxuA9sTnH9dqI5aRqy5ifDjuya7Emm8sdOUvtGdmn
|
||||
SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
|
||||
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
|
||||
jw4YcO5u
|
||||
=r3UU
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
ISO-8859-1`
|
||||
|
||||
sha := SHA1{0xfe, 0xaf, 0x4b, 0xa6, 0xbc, 0x63, 0x5f, 0xec, 0x44, 0x2f, 0x46, 0xdd, 0xd4, 0x51, 0x24, 0x16, 0xec, 0x43, 0xc2, 0xc2}
|
||||
gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, gitRepo)
|
||||
defer gitRepo.Close()
|
||||
|
||||
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotNil(t, commitFromReader) {
|
||||
return
|
||||
}
|
||||
assert.EqualValues(t, sha, commitFromReader.ID)
|
||||
assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow
|
||||
Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR
|
||||
gizKa2COiGtugv8fE+TKqXKaJx6uJUJEjaBd8E9Af9PrAzjWj+A84lU6/PgPS8hq
|
||||
zOfZraLOEWRH4tZcS+u2yFLu3ez2Wqh1xW5LNy7xqEedMXEFD1HwSJ0+pjacNkzr
|
||||
frp6Asyt7xRI6YmgFJZJoRsS3Ktr6rtKeRL2IErSQQyorOqj6gKrglhrhfG/114j
|
||||
FKB1v4or0WZ1DE8iP2SJZ3n+/K1IuWAINh7MVdb7PndfBPEa+IL+ucNk5uzEE8Jd
|
||||
G8smGxXUeFEt2cP1dj2W8EgAxuA9sTnH9dqI5aRqy5ifDjuya7Emm8sdOUvtGdmn
|
||||
SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
|
||||
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
|
||||
jw4YcO5u
|
||||
=r3UU
|
||||
-----END PGP SIGNATURE-----
|
||||
`, commitFromReader.Signature.Signature)
|
||||
assert.EqualValues(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
|
||||
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
|
||||
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
encoding ISO-8859-1
|
||||
|
||||
ISO-8859-1`, commitFromReader.Signature.Payload)
|
||||
assert.EqualValues(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String())
|
||||
|
||||
commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n"))
|
||||
assert.NoError(t, err)
|
||||
commitFromReader.CommitMessage += "\n\n"
|
||||
commitFromReader.Signature.Payload += "\n\n"
|
||||
assert.EqualValues(t, commitFromReader, commitFromReader2)
|
||||
}
|
||||
|
||||
func TestHasPreviousCommit(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// ParsePaginator parses a db.Paginator into a skip and limit
|
||||
func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
|
||||
func ParsePaginator(paginator *db.ListOptions, max ...int) (int, int) {
|
||||
// Use a very large number to indicate no limit
|
||||
unlimited := math.MaxInt32
|
||||
if len(max) > 0 {
|
||||
@@ -19,22 +19,15 @@ func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
|
||||
}
|
||||
|
||||
if paginator == nil || paginator.IsListAll() {
|
||||
// It shouldn't happen. In actual usage scenarios, there should not be requests to search all.
|
||||
// But if it does happen, respect it and return "unlimited".
|
||||
// And it's also useful for testing.
|
||||
return 0, unlimited
|
||||
}
|
||||
|
||||
// Warning: Do not use GetSkipTake() for *db.ListOptions
|
||||
// Its implementation could reset the page size with setting.API.MaxResponseItems
|
||||
if listOptions, ok := paginator.(*db.ListOptions); ok {
|
||||
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
|
||||
var start int
|
||||
if listOptions.Page == 0 {
|
||||
start = 0
|
||||
} else {
|
||||
start = (listOptions.Page - 1) * listOptions.PageSize
|
||||
}
|
||||
return start, listOptions.PageSize
|
||||
}
|
||||
return 0, unlimited
|
||||
if paginator.PageSize == 0 {
|
||||
// Do not return any results when searching, it's used to get the total count only.
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return paginator.GetSkipTake()
|
||||
|
||||
@@ -78,6 +78,17 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If pagesize == 0, return total count only. It's a special case for search count.
|
||||
if options.Paginator != nil && options.Paginator.PageSize == 0 {
|
||||
total, err := issue_model.CountIssues(ctx, opt, cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &internal.SearchResult{
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
ids, total, err := issue_model.IssueIDs(ctx, opt, cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -309,7 +309,7 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
|
||||
|
||||
// CountIssues counts issues by options. It is a shortcut of SearchIssues(ctx, opts) but only returns the total count.
|
||||
func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
|
||||
opts = opts.Copy(func(options *SearchOptions) { opts.Paginator = &db_model.ListOptions{PageSize: 0} })
|
||||
opts = opts.Copy(func(options *SearchOptions) { options.Paginator = &db_model.ListOptions{PageSize: 0} })
|
||||
|
||||
_, total, err := SearchIssues(ctx, opts)
|
||||
return total, err
|
||||
|
||||
@@ -104,7 +104,7 @@ type SearchOptions struct {
|
||||
UpdatedAfterUnix *int64
|
||||
UpdatedBeforeUnix *int64
|
||||
|
||||
db.Paginator
|
||||
Paginator *db.ListOptions
|
||||
|
||||
SortBy SortBy // sort by field
|
||||
}
|
||||
|
||||
@@ -77,6 +77,13 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
|
||||
assert.Equal(t, c.ExpectedIDs, ids)
|
||||
assert.Equal(t, c.ExpectedTotal, result.Total)
|
||||
}
|
||||
|
||||
// test counting
|
||||
c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0}
|
||||
countResult, err := indexer.Search(context.Background(), c.SearchOptions)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, countResult.Hits)
|
||||
assert.Equal(t, result.Total, countResult.Total)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +211,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
|
||||
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)
|
||||
|
||||
counting := limit == 0
|
||||
if counting {
|
||||
// If set limit to 0, it will be 20 by default, and -1 is not allowed.
|
||||
// See https://www.meilisearch.com/docs/reference/api/search#limit
|
||||
// So set limit to 1 to make the cost as low as possible, then clear the result before returning.
|
||||
limit = 1
|
||||
}
|
||||
|
||||
// to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s)
|
||||
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
|
||||
keyword := doubleQuoteKeyword(options.Keyword)
|
||||
@@ -226,6 +234,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if counting {
|
||||
searchRes.Hits = nil
|
||||
}
|
||||
|
||||
hits := make([]internal.Match, 0, len(searchRes.Hits))
|
||||
for _, hit := range searchRes.Hits {
|
||||
hits = append(hits, internal.Match{
|
||||
|
||||
@@ -44,7 +44,7 @@ func (c *FilesystemClient) Download(ctx context.Context, objects []Pointer, call
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
if err := callback(p, f, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func (c *FilesystemClient) Upload(ctx context.Context, objects []Pointer, callba
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, content)
|
||||
|
||||
return err
|
||||
|
||||
@@ -503,9 +503,17 @@ func TestMathBlock(t *testing.T) {
|
||||
`\(a\) \(b\)`,
|
||||
`<p><code class="language-math is-loading">a</code> <code class="language-math is-loading">b</code></p>` + nl,
|
||||
},
|
||||
{
|
||||
`$a$.`,
|
||||
`<p><code class="language-math is-loading">a</code>.</p>` + nl,
|
||||
},
|
||||
{
|
||||
`.$a$`,
|
||||
`<p>.$a$</p>` + nl,
|
||||
},
|
||||
{
|
||||
`$a a$b b$`,
|
||||
`<p><code class="language-math is-loading">a a$b b</code></p>` + nl,
|
||||
`<p>$a a$b b$</p>` + nl,
|
||||
},
|
||||
{
|
||||
`a a$b b`,
|
||||
@@ -513,7 +521,15 @@ func TestMathBlock(t *testing.T) {
|
||||
},
|
||||
{
|
||||
`a$b $a a$b b$`,
|
||||
`<p>a$b <code class="language-math is-loading">a a$b b</code></p>` + nl,
|
||||
`<p>a$b $a a$b b$</p>` + nl,
|
||||
},
|
||||
{
|
||||
"a$x$",
|
||||
`<p>a$x$</p>` + nl,
|
||||
},
|
||||
{
|
||||
"$x$a",
|
||||
`<p>$x$a</p>` + nl,
|
||||
},
|
||||
{
|
||||
"$$a$$",
|
||||
|
||||
@@ -41,9 +41,12 @@ func (parser *inlineParser) Trigger() []byte {
|
||||
return parser.start[0:1]
|
||||
}
|
||||
|
||||
func isPunctuation(b byte) bool {
|
||||
return b == '.' || b == '!' || b == '?' || b == ',' || b == ';' || b == ':'
|
||||
}
|
||||
|
||||
func isAlphanumeric(b byte) bool {
|
||||
// Github only cares about 0-9A-Za-z
|
||||
return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z')
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
|
||||
}
|
||||
|
||||
// Parse parses the current line and returns a result of parsing.
|
||||
@@ -56,7 +59,7 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
|
||||
}
|
||||
|
||||
precedingCharacter := block.PrecendingCharacter()
|
||||
if precedingCharacter < 256 && isAlphanumeric(byte(precedingCharacter)) {
|
||||
if precedingCharacter < 256 && (isAlphanumeric(byte(precedingCharacter)) || isPunctuation(byte(precedingCharacter))) {
|
||||
// need to exclude things like `a$` from being considered a start
|
||||
return nil
|
||||
}
|
||||
@@ -75,14 +78,19 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
|
||||
ender += pos
|
||||
|
||||
// Now we want to check the character at the end of our parser section
|
||||
// that is ender + len(parser.end)
|
||||
// that is ender + len(parser.end) and check if char before ender is '\'
|
||||
pos = ender + len(parser.end)
|
||||
if len(line) <= pos {
|
||||
break
|
||||
}
|
||||
if !isAlphanumeric(line[pos]) {
|
||||
suceedingCharacter := line[pos]
|
||||
if !isPunctuation(suceedingCharacter) && !(suceedingCharacter == ' ') {
|
||||
return nil
|
||||
}
|
||||
if line[ender-1] != '\\' {
|
||||
break
|
||||
}
|
||||
|
||||
// move the pointer onwards
|
||||
ender += len(parser.end)
|
||||
}
|
||||
|
||||
+15
-10
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Git environment variables
|
||||
@@ -32,13 +33,13 @@ const (
|
||||
)
|
||||
|
||||
// Bool checks for a key in the map and parses as a boolean
|
||||
func (g GitPushOptions) Bool(key string, def bool) bool {
|
||||
func (g GitPushOptions) Bool(key string) util.OptionalBool {
|
||||
if val, ok := g[key]; ok {
|
||||
if b, err := strconv.ParseBool(val); err == nil {
|
||||
return b
|
||||
return util.OptionalBoolOf(b)
|
||||
}
|
||||
}
|
||||
return def
|
||||
return util.OptionalBoolNone
|
||||
}
|
||||
|
||||
// HookOptions represents the options for the Hook calls
|
||||
@@ -87,13 +88,17 @@ type HookProcReceiveResult struct {
|
||||
|
||||
// HookProcReceiveRefResult represents an individual result from ProcReceive
|
||||
type HookProcReceiveRefResult struct {
|
||||
OldOID string
|
||||
NewOID string
|
||||
Ref string
|
||||
OriginalRef git.RefName
|
||||
IsForcePush bool
|
||||
IsNotMatched bool
|
||||
Err string
|
||||
OldOID string
|
||||
NewOID string
|
||||
Ref string
|
||||
OriginalRef git.RefName
|
||||
IsForcePush bool
|
||||
IsNotMatched bool
|
||||
Err string
|
||||
IsCreatePR bool
|
||||
URL string
|
||||
ShouldShowMessage bool
|
||||
HeadBranch string
|
||||
}
|
||||
|
||||
// HookPreReceive check whether the provided commits are allowed
|
||||
|
||||
@@ -6,6 +6,9 @@ package session
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
)
|
||||
|
||||
@@ -18,6 +21,10 @@ type Store interface {
|
||||
|
||||
// RegenerateSession regenerates the underlying session and returns the new store
|
||||
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
|
||||
// Ensure that a cookie with a trailing slash does not take precedence over
|
||||
// the cookie written by the middleware.
|
||||
middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
||||
|
||||
s, err := session.RegenerateSession(resp, req)
|
||||
return s, err
|
||||
}
|
||||
|
||||
@@ -142,35 +142,39 @@ type remoteAddress struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
|
||||
a := remoteAddress{}
|
||||
|
||||
remoteURL := m.OriginalURL
|
||||
if ignoreOriginalURL || remoteURL == "" {
|
||||
var err error
|
||||
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
|
||||
if err != nil {
|
||||
log.Error("GetRemoteURL %v", err)
|
||||
return a
|
||||
}
|
||||
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
|
||||
ret := remoteAddress{}
|
||||
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
|
||||
if err != nil {
|
||||
log.Error("GetRemoteURL %v", err)
|
||||
return ret
|
||||
}
|
||||
|
||||
u, err := giturl.Parse(remoteURL)
|
||||
if err != nil {
|
||||
log.Error("giturl.Parse %v", err)
|
||||
return a
|
||||
return ret
|
||||
}
|
||||
|
||||
if u.Scheme != "ssh" && u.Scheme != "file" {
|
||||
if u.User != nil {
|
||||
a.Username = u.User.Username()
|
||||
a.Password, _ = u.User.Password()
|
||||
ret.Username = u.User.Username()
|
||||
ret.Password, _ = u.User.Password()
|
||||
}
|
||||
u.User = nil
|
||||
}
|
||||
a.Address = u.String()
|
||||
|
||||
return a
|
||||
// The URL stored in the git repo could contain authentication,
|
||||
// erase it, or it will be shown in the UI.
|
||||
u.User = nil
|
||||
ret.Address = u.String()
|
||||
// Why not use m.OriginalURL to set ret.Address?
|
||||
// It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository.
|
||||
// However, the old code has already stored authentication in m.OriginalURL when updating mirror settings.
|
||||
// That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased.
|
||||
// Instead of doing this, why not directly use the authentication-erased URL from the Git repository?
|
||||
// It should be the same as long as there are no bugs.
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func FilenameIsImage(filename string) bool {
|
||||
|
||||
@@ -45,10 +45,32 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
|
||||
SameSite: setting.SessionConfig.SameSite,
|
||||
}
|
||||
resp.Header().Add("Set-Cookie", cookie.String())
|
||||
if maxAge < 0 {
|
||||
// There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "".
|
||||
// So we have to delete the cookie on path="" again, because some old code leaves cookies on path="".
|
||||
cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/")
|
||||
resp.Header().Add("Set-Cookie", cookie.String())
|
||||
}
|
||||
// Previous versions would use a cookie path with a trailing /.
|
||||
// These are more specific than cookies without a trailing /, so
|
||||
// we need to delete these if they exist.
|
||||
DeleteLegacySiteCookie(resp, name)
|
||||
}
|
||||
|
||||
// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
|
||||
// path with a trailing /, which would unintentionally override the cookie.
|
||||
func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
||||
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
|
||||
// If the cookie path ends with /, no legacy cookies will take
|
||||
// precedence, so do nothing. The exception is that cookies with no
|
||||
// path could override other cookies, but it's complicated and we don't
|
||||
// currently handle that.
|
||||
return
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: name,
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Path: setting.SessionConfig.CookiePath + "/",
|
||||
Domain: setting.SessionConfig.Domain,
|
||||
Secure: setting.SessionConfig.Secure,
|
||||
HttpOnly: true,
|
||||
SameSite: setting.SessionConfig.SameSite,
|
||||
}
|
||||
resp.Header().Add("Set-Cookie", cookie.String())
|
||||
}
|
||||
|
||||
Generated
+4
-4
@@ -27,7 +27,7 @@
|
||||
"escape-goat": "4.0.0",
|
||||
"fast-glob": "3.3.1",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.9",
|
||||
"katex": "0.16.10",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"lightningcss-loader": "2.1.0",
|
||||
"mermaid": "10.6.1",
|
||||
@@ -6727,9 +6727,9 @@
|
||||
"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.9",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz",
|
||||
"integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==",
|
||||
"version": "0.16.10",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
|
||||
"integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
"escape-goat": "4.0.0",
|
||||
"fast-glob": "3.3.1",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.9",
|
||||
"katex": "0.16.10",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"lightningcss-loader": "2.1.0",
|
||||
"mermaid": "10.6.1",
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -54,6 +56,18 @@ func (s *Service) Register(
|
||||
return nil, errors.New("runner registration token has been invalidated, please use the latest one")
|
||||
}
|
||||
|
||||
if runnerToken.OwnerID > 0 {
|
||||
if _, err := user_model.GetUserByID(ctx, runnerToken.OwnerID); err != nil {
|
||||
return nil, errors.New("owner of the token not found")
|
||||
}
|
||||
}
|
||||
|
||||
if runnerToken.RepoID > 0 {
|
||||
if _, err := repo_model.GetRepositoryByID(ctx, runnerToken.RepoID); err != nil {
|
||||
return nil, errors.New("repository of the token not found")
|
||||
}
|
||||
}
|
||||
|
||||
labels := req.Msg.Labels
|
||||
// TODO: agent_labels should be removed from pb after Gitea 1.20 released.
|
||||
// Old version runner's agent_labels slice is not empty and labels slice is empty.
|
||||
|
||||
@@ -6,10 +6,11 @@ package private
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@@ -89,8 +90,10 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
}
|
||||
}
|
||||
|
||||
isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate)
|
||||
isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate)
|
||||
// Handle Push Options
|
||||
if len(opts.GitPushOptions) > 0 {
|
||||
if !isPrivate.IsNone() || !isTemplate.IsNone() {
|
||||
// load the repository
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
@@ -101,13 +104,49 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
wasEmpty = repo.IsEmpty
|
||||
}
|
||||
|
||||
repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate)
|
||||
repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate)
|
||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, "is_private", "is_template"); err != nil {
|
||||
pusher, err := user_model.GetUserByID(ctx, opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, pusher)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if !perm.IsOwner() && !perm.IsAdmin() {
|
||||
ctx.JSON(http.StatusNotFound, private.HookPostReceiveResult{
|
||||
Err: "Permissions denied",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
cols := make([]string, 0, len(opts.GitPushOptions))
|
||||
|
||||
if !isPrivate.IsNone() {
|
||||
repo.IsPrivate = isPrivate.IsTrue()
|
||||
cols = append(cols, "is_private")
|
||||
}
|
||||
|
||||
if !isTemplate.IsNone() {
|
||||
repo.IsTemplate = isTemplate.IsTrue()
|
||||
cols = append(cols, "is_template")
|
||||
}
|
||||
|
||||
if len(cols) > 0 {
|
||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, cols...); err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,42 +161,6 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
refFullName := opts.RefFullNames[i]
|
||||
newCommitID := opts.NewCommitIDs[i]
|
||||
|
||||
// post update for agit pull request
|
||||
// FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR
|
||||
if git.SupportProcReceive && refFullName.IsPull() {
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pullIndex, _ := strconv.ParseInt(refFullName.PullName(), 10, 64)
|
||||
if pullIndex <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetPullRequestByIndex(ctx, repo.ID, pullIndex)
|
||||
if err != nil && !issues_model.IsErrPullRequestNotExist(err) {
|
||||
log.Error("Failed to get PR by index %v Error: %v", pullIndex, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Failed to get PR by index %v Error: %v", pullIndex, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if pr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, private.HookPostReceiveBranchResult{
|
||||
Message: setting.Git.PullRequestPushMessage && repo.AllowsPulls(),
|
||||
Create: false,
|
||||
Branch: "",
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// If we've pushed a branch (and not deleted it)
|
||||
if newCommitID != git.EmptySHA && refFullName.IsBranch() {
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -44,20 +45,17 @@ func RenderFile(ctx *context.Context) {
|
||||
isTextFile := st.IsText()
|
||||
|
||||
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
|
||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||
|
||||
if markupType := markup.Type(blob.Name()); markupType == "" {
|
||||
if isTextFile {
|
||||
_, err = io.Copy(ctx.Resp, rd)
|
||||
if err != nil {
|
||||
ctx.ServerError("Copy", err)
|
||||
}
|
||||
return
|
||||
_, _ = io.Copy(ctx.Resp, rd)
|
||||
} else {
|
||||
http.Error(ctx.Resp, "Unsupported file type render", http.StatusInternalServerError)
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "Unsupported file type render")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||
err = markup.Render(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
RelativePath: ctx.Repo.TreePath,
|
||||
@@ -71,7 +69,8 @@ func RenderFile(ctx *context.Context) {
|
||||
InStandalonePage: true,
|
||||
}, rd, ctx.Resp)
|
||||
if err != nil {
|
||||
ctx.ServerError("Render", err)
|
||||
log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err)
|
||||
http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,13 @@ func RenameBranchPost(ctx *context.Context) {
|
||||
|
||||
msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
|
||||
if err != nil {
|
||||
ctx.ServerError("RenameBranch", err)
|
||||
switch {
|
||||
case git_model.IsErrBranchAlreadyExists(err):
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To))
|
||||
ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
|
||||
default:
|
||||
ctx.ServerError("RenameBranch", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
|
||||
|
||||
"github.com/nektos/act/pkg/jobparser"
|
||||
)
|
||||
@@ -114,19 +116,20 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
}
|
||||
|
||||
creator := user_model.NewActionsUser()
|
||||
if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
|
||||
Repo: repo,
|
||||
SHA: sha,
|
||||
Creator: creator,
|
||||
CommitStatus: &git_model.CommitStatus{
|
||||
SHA: sha,
|
||||
commitID, err := git.NewIDFromString(sha)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err)
|
||||
}
|
||||
if err := commitstatus_service.CreateCommitStatus(ctx, repo, creator,
|
||||
commitID.String(),
|
||||
&git_model.CommitStatus{
|
||||
SHA: commitID.String(),
|
||||
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index),
|
||||
Description: description,
|
||||
Context: ctxname,
|
||||
CreatorID: creator.ID,
|
||||
State: state,
|
||||
},
|
||||
}); err != nil {
|
||||
}); err != nil {
|
||||
return fmt.Errorf("NewCommitStatus: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -494,12 +494,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository)
|
||||
}
|
||||
|
||||
// We need a notifyInput to call handleSchedules
|
||||
// Here we use the commit author as the Doer of the notifyInput
|
||||
commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get user by email: %w", err)
|
||||
}
|
||||
notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule)
|
||||
// if repo is a mirror, commit author maybe an external user,
|
||||
// so we use action user as the Doer of the notifyInput
|
||||
notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule)
|
||||
|
||||
return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch)
|
||||
}
|
||||
|
||||
+17
-9
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
)
|
||||
@@ -149,10 +150,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
|
||||
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
|
||||
|
||||
results = append(results, private.HookProcReceiveRefResult{
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
OldOID: git.EmptySHA,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
OldOID: git.EmptySHA,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
IsCreatePR: true,
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(),
|
||||
HeadBranch: headBranch,
|
||||
})
|
||||
continue
|
||||
}
|
||||
@@ -214,11 +219,14 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
|
||||
isForcePush := comment != nil && comment.IsForcePush
|
||||
|
||||
results = append(results, private.HookProcReceiveRefResult{
|
||||
OldOID: oldCommitID,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
IsForcePush: isForcePush,
|
||||
OldOID: oldCommitID,
|
||||
NewOID: opts.NewCommitIDs[i],
|
||||
Ref: pr.GetGitRefName(),
|
||||
OriginalRef: opts.RefFullNames[i],
|
||||
IsForcePush: isForcePush,
|
||||
IsCreatePR: false,
|
||||
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
|
||||
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
session_module "code.gitea.io/gitea/modules/session"
|
||||
|
||||
chiSession "gitea.com/go-chi/session"
|
||||
"github.com/gorilla/sessions"
|
||||
@@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s
|
||||
chiStore := chiSession.GetSession(r)
|
||||
|
||||
if session.IsNew {
|
||||
_, _ = chiSession.RegenerateSession(w, r)
|
||||
_, _ = session_module.RegenerateSession(w, r)
|
||||
session.IsNew = false
|
||||
}
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pr.HeadRepo.IsFork {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pr.BaseRepo.IsFork {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
repo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string {
|
||||
// NewGitBucketDownloader creates a GitBucket downloader
|
||||
func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader {
|
||||
githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName)
|
||||
// Gitbucket 4.40 uses different internal hard-coded perPage values.
|
||||
// Issues, PRs, and other major parts use 25. Release page uses 10.
|
||||
// Some API doesn't support paging yet. Sounds difficult, but using
|
||||
// minimum number among them worked out very well.
|
||||
githubDownloader.maxPerPage = 10
|
||||
githubDownloader.SkipReactions = true
|
||||
githubDownloader.SkipReviews = true
|
||||
return &GitBucketDownloader{
|
||||
|
||||
@@ -250,14 +250,13 @@ func migrateRepository(ctx context.Context, doer *user_model.User, downloader ba
|
||||
}
|
||||
log.Warn("migrating milestones is not supported, ignored")
|
||||
}
|
||||
|
||||
msBatchSize := uploader.MaxBatchInsertSize("milestone")
|
||||
for len(milestones) > 0 {
|
||||
if len(milestones) < msBatchSize {
|
||||
msBatchSize = len(milestones)
|
||||
}
|
||||
|
||||
if err := uploader.CreateMilestones(milestones...); err != nil {
|
||||
if err := uploader.CreateMilestones(milestones[:msBatchSize]...); err != nil {
|
||||
return err
|
||||
}
|
||||
milestones = milestones[msBatchSize:]
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
@@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000"
|
||||
|
||||
// UpdateAddress writes new address to Git repository and database
|
||||
func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error {
|
||||
u, err := giturl.Parse(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid addr: %v", err)
|
||||
}
|
||||
|
||||
remoteName := m.GetRemoteName()
|
||||
repoPath := m.GetRepository(ctx).RepoPath()
|
||||
// Remove old remote
|
||||
_, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||
return err
|
||||
}
|
||||
@@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
||||
}
|
||||
}
|
||||
|
||||
m.Repo.OriginalURL = addr
|
||||
// erase authentication before storing in database
|
||||
u.User = nil
|
||||
m.Repo.OriginalURL = u.String()
|
||||
return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url")
|
||||
}
|
||||
|
||||
@@ -449,19 +457,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var gitRepo *git.Repository
|
||||
if len(results) == 0 {
|
||||
log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
|
||||
} else {
|
||||
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
|
||||
gitRepo, err = git.OpenRepository(ctx, m.Repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
gitRepo, err := git.OpenRepository(ctx, m.Repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
|
||||
if len(results) > 0 {
|
||||
if ok := checkAndUpdateEmptyRepository(m, gitRepo, results); !ok {
|
||||
log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -533,16 +539,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
||||
}
|
||||
log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo)
|
||||
|
||||
// Get latest commit date and update to current repository updated time
|
||||
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
|
||||
isEmpty, err := gitRepo.IsEmpty()
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
if !isEmpty {
|
||||
// Get latest commit date and update to current repository updated time
|
||||
commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
|
||||
if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/services/automerge"
|
||||
@@ -24,15 +25,47 @@ func getCacheKey(repoID int64, brancheName string) string {
|
||||
return fmt.Sprintf("commit_status:%x", hashBytes)
|
||||
}
|
||||
|
||||
func updateCommitStatusCache(ctx context.Context, repoID int64, branchName string, status api.CommitStatusState) error {
|
||||
type commitStatusCacheValue struct {
|
||||
State string `json:"state"`
|
||||
TargetURL string `json:"target_url"`
|
||||
}
|
||||
|
||||
func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue {
|
||||
c := cache.GetCache()
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Put(getCacheKey(repoID, branchName), string(status), 3*24*60)
|
||||
statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string)
|
||||
if ok && statusStr != "" {
|
||||
var cv commitStatusCacheValue
|
||||
err := json.Unmarshal([]byte(statusStr), &cv)
|
||||
if err == nil && cv.State != "" {
|
||||
return &cv
|
||||
}
|
||||
if err != nil {
|
||||
log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteCommitStatusCache(ctx context.Context, repoID int64, branchName string) error {
|
||||
func updateCommitStatusCache(repoID int64, branchName string, state api.CommitStatusState, targetURL string) error {
|
||||
c := cache.GetCache()
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
bs, err := json.Marshal(commitStatusCacheValue{
|
||||
State: state.String(),
|
||||
TargetURL: targetURL,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn("updateCommitStatusCache: json.Marshal failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
return c.Put(getCacheKey(repoID, branchName), string(bs), 3*24*60)
|
||||
}
|
||||
|
||||
func deleteCommitStatusCache(repoID int64, branchName string) error {
|
||||
c := cache.GetCache()
|
||||
if c == nil {
|
||||
return nil
|
||||
@@ -76,7 +109,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
|
||||
}
|
||||
|
||||
if commit.ID.String() == defaultBranchCommit.ID.String() { // since one commit status updated, the combined commit status should be invalid
|
||||
if err := deleteCommitStatusCache(ctx, repo.ID, repo.DefaultBranch); err != nil {
|
||||
if err := deleteCommitStatusCache(repo.ID, repo.DefaultBranch); err != nil {
|
||||
log.Error("deleteCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
|
||||
}
|
||||
}
|
||||
@@ -93,12 +126,11 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
|
||||
// FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache
|
||||
func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) {
|
||||
results := make([]*git_model.CommitStatus, len(repos))
|
||||
c := cache.GetCache()
|
||||
if c != nil {
|
||||
for i, repo := range repos {
|
||||
status, ok := c.Get(getCacheKey(repo.ID, repo.DefaultBranch)).(string)
|
||||
if ok && status != "" {
|
||||
results[i] = &git_model.CommitStatus{State: api.CommitStatusState(status)}
|
||||
for i, repo := range repos {
|
||||
if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil {
|
||||
results[i] = &git_model.CommitStatus{
|
||||
State: api.CommitStatusState(cv.State),
|
||||
TargetURL: cv.TargetURL,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +159,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
|
||||
if results[i] == nil {
|
||||
results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID])
|
||||
if results[i].State != "" {
|
||||
if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil {
|
||||
if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil {
|
||||
log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, r
|
||||
&actions_model.ActionScheduleSpec{RepoID: repoID},
|
||||
&actions_model.ActionSchedule{RepoID: repoID},
|
||||
&actions_model.ActionArtifact{RepoID: repoID},
|
||||
&actions_model.ActionRunnerToken{RepoID: repoID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %w", err)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
|
||||
&pull_model.ReviewState{UserID: u.ID},
|
||||
&user_model.Redirect{RedirectUserID: u.ID},
|
||||
&actions_model.ActionRunner{OwnerID: u.ID},
|
||||
&actions_model.ActionRunnerToken{OwnerID: u.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %w", err)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) {
|
||||
err := hook.SetHeaderAuthorization("Bearer s3cr3t-t0ken")
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook))
|
||||
db.GetEngine(db.DefaultContext).NoAutoTime().DB().Logger.ShowSQL(true)
|
||||
|
||||
hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush, Payloader: &api.PushPayload{}}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<button class="ui tiny red button">
|
||||
{{svg "octicon-warning" 14}} CJK文本测试
|
||||
{{svg "octicon-alert" 14}} CJK文本测试
|
||||
</button>
|
||||
<button class="ui tiny primary button">
|
||||
{{svg "octicon-info" 14}} Button
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<button class="ui tiny red button">
|
||||
{{svg "octicon-warning" 12}} CJK文本测试 <!-- single CJK text test, it shouldn't be horizontal -->
|
||||
{{svg "octicon-alert" 12}} CJK文本测试 <!-- single CJK text test, it shouldn't be horizontal -->
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
)}}
|
||||
|
||||
<div class="field footer gt-mx-3">
|
||||
<span class="markup-info">{{svg "octicon-markup"}} {{ctx.Locale.Tr "repo.diff.comment.markdown_info"}}</span>
|
||||
<span class="markup-info">{{svg "octicon-markdown"}} {{ctx.Locale.Tr "repo.diff.comment.markdown_info"}}</span>
|
||||
<div class="gt-text-right">
|
||||
{{if $.reply}}
|
||||
<button class="ui submit primary tiny button btn-reply" type="submit">{{ctx.Locale.Tr "repo.diff.comment.reply"}}</button>
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
<label for="interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
|
||||
<input id="interval" name="interval" value="{{.PullMirror.Interval}}">
|
||||
</div>
|
||||
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}}
|
||||
{{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
|
||||
<div class="field {{if .Err_MirrorAddress}}error{{end}}">
|
||||
<label for="mirror_address">{{ctx.Locale.Tr "repo.mirror_address"}}</label>
|
||||
<input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGitPush(t *testing.T) {
|
||||
onGiteaRun(t, testGitPush)
|
||||
}
|
||||
|
||||
func testGitPush(t *testing.T, u *url.URL) {
|
||||
t.Run("Push branch with options", func(t *testing.T) {
|
||||
runTestGitPush(t, u, func(t *testing.T, gitPath string) {
|
||||
branchName := "branch-with-options"
|
||||
doGitCreateBranch(gitPath, branchName)(t)
|
||||
doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gitPath string)) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
||||
Name: "repo-to-push",
|
||||
Description: "test git push",
|
||||
AutoInit: false,
|
||||
DefaultBranch: "main",
|
||||
IsPrivate: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, repo)
|
||||
|
||||
gitPath := t.TempDir()
|
||||
|
||||
doGitInitTestRepository(gitPath)(t)
|
||||
|
||||
oldPath := u.Path
|
||||
oldUser := u.User
|
||||
defer func() {
|
||||
u.Path = oldPath
|
||||
u.User = oldUser
|
||||
}()
|
||||
u.Path = repo.FullName() + ".git"
|
||||
u.User = url.UserPassword(user.LowerName, userPassword)
|
||||
|
||||
doGitAddRemote(gitPath, "origin", u)(t)
|
||||
|
||||
gitRepo, err := git.OpenRepository(git.DefaultContext, gitPath)
|
||||
require.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
|
||||
gitOperation(t, gitPath)
|
||||
|
||||
require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, user.ID, repo.ID))
|
||||
}
|
||||
@@ -62,6 +62,30 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, titl
|
||||
return resp
|
||||
}
|
||||
|
||||
func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
|
||||
headCompare := headBranch
|
||||
if headRepoOwner != "" {
|
||||
if headRepoName != "" {
|
||||
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
|
||||
} else {
|
||||
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
|
||||
}
|
||||
}
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Submit the form for creating the pull
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"title": title,
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
return resp
|
||||
}
|
||||
|
||||
func TestPullCreate(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
@@ -159,10 +159,18 @@ func TestPullView_CodeOwner(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
session := loginUser(t, "user5")
|
||||
createPullRequest(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "codeowner-basebranch-forked", "Test Pull Request2")
|
||||
|
||||
// create a pull request on the forked repository, code reviewers should not be mentioned
|
||||
testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository")
|
||||
|
||||
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
|
||||
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
|
||||
|
||||
// create a pull request to base repository, code reviewers should be mentioned
|
||||
testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3")
|
||||
|
||||
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
|
||||
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,17 +5,23 @@ package integration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRenameBranch(t *testing.T) {
|
||||
onGiteaRun(t, testRenameBranch)
|
||||
}
|
||||
|
||||
func testRenameBranch(t *testing.T, u *url.URL) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: 1, Name: "master"})
|
||||
@@ -26,20 +32,19 @@ func TestRenameBranch(t *testing.T) {
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
postData := map[string]string{
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"from": "master",
|
||||
"to": "main",
|
||||
}
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", postData)
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// check new branch link
|
||||
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", postData)
|
||||
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", nil)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// check old branch link
|
||||
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", postData)
|
||||
req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", nil)
|
||||
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
location := resp.Header().Get("Location")
|
||||
assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location)
|
||||
@@ -47,4 +52,69 @@ func TestRenameBranch(t *testing.T) {
|
||||
// check db
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.Equal(t, "main", repo1.DefaultBranch)
|
||||
|
||||
// create branch1
|
||||
csrf := GetCSRF(t, session, "/user2/repo1/src/branch/main")
|
||||
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
|
||||
"_csrf": csrf,
|
||||
"new_branch_name": "branch1",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
branch1 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
||||
assert.Equal(t, "branch1", branch1.Name)
|
||||
|
||||
// create branch2
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{
|
||||
"_csrf": csrf,
|
||||
"new_branch_name": "branch2",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
branch2 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||
assert.Equal(t, "branch2", branch2.Name)
|
||||
|
||||
// rename branch2 to branch1
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"from": "branch2",
|
||||
"to": "branch1",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||
assert.NotNil(t, flashCookie)
|
||||
assert.Contains(t, flashCookie.Value, "error")
|
||||
|
||||
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||
assert.Equal(t, "branch2", branch2.Name)
|
||||
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
||||
assert.Equal(t, "branch1", branch1.Name)
|
||||
|
||||
// delete branch1
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete", map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"name": "branch1",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||
assert.Equal(t, "branch2", branch2.Name)
|
||||
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
||||
assert.True(t, branch1.IsDeleted) // virtual deletion
|
||||
|
||||
// rename branch2 to branch1 again
|
||||
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"from": "branch2",
|
||||
"to": "branch1",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
|
||||
assert.NotNil(t, flashCookie)
|
||||
assert.Contains(t, flashCookie.Value, "success")
|
||||
|
||||
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
||||
assert.Equal(t, "branch1", branch1.Name)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user