mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'main' into allow-force-push-protected-branches
This commit is contained in:
+2
-2
@@ -157,10 +157,10 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseCount(ctx context.Context, id int64) (int64, error) {
|
func getReleaseCount(ctx context.Context, id int64) (int64, error) {
|
||||||
return repo_model.GetReleaseCountByRepoID(
|
return db.Count[repo_model.Release](
|
||||||
ctx,
|
ctx,
|
||||||
id,
|
|
||||||
repo_model.FindReleasesOptions{
|
repo_model.FindReleasesOptions{
|
||||||
|
RepoID: id,
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,17 +27,18 @@ The following examples use `dnf`.
|
|||||||
To register the RPM registry add the url to the list of known apt sources:
|
To register the RPM registry add the url to the list of known apt sources:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
|
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
|
||||||
```
|
```
|
||||||
|
|
||||||
| Placeholder | Description |
|
| Placeholder | Description |
|
||||||
| ----------- | ----------- |
|
| ----------- |----------------------------------------------------|
|
||||||
| `owner` | The owner of the package. |
|
| `owner` | The owner of the package. |
|
||||||
|
| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.|
|
||||||
|
|
||||||
If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication):
|
If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
|
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
|
||||||
```
|
```
|
||||||
|
|
||||||
You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.repos.d` too.
|
You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.repos.d` too.
|
||||||
@@ -47,19 +48,20 @@ You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.
|
|||||||
To publish a RPM package (`*.rpm`), perform a HTTP PUT operation with the package content in the request body.
|
To publish a RPM package (`*.rpm`), perform a HTTP PUT operation with the package content in the request body.
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
|
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
| --------- | ----------- |
|
| --------- | ----------- |
|
||||||
| `owner` | The owner of the package. |
|
| `owner` | The owner of the package. |
|
||||||
|
| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.|
|
||||||
|
|
||||||
Example request using HTTP Basic authentication:
|
Example request using HTTP Basic authentication:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --user your_username:your_password_or_token \
|
curl --user your_username:your_password_or_token \
|
||||||
--upload-file path/to/file.rpm \
|
--upload-file path/to/file.rpm \
|
||||||
https://gitea.example.com/api/packages/testuser/rpm/upload
|
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/upload
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are using 2FA or OAuth use a [personal access token](development/api-usage.md#authentication) instead of the password.
|
If you are using 2FA or OAuth use a [personal access token](development/api-usage.md#authentication) instead of the password.
|
||||||
@@ -78,12 +80,13 @@ The server responds with the following HTTP Status codes.
|
|||||||
To delete an RPM package perform a HTTP DELETE operation. This will delete the package version too if there is no file left.
|
To delete an RPM package perform a HTTP DELETE operation. This will delete the package version too if there is no file left.
|
||||||
|
|
||||||
```
|
```
|
||||||
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
|
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
| ----------------- | ----------- |
|
|-------------------|----------------------------|
|
||||||
| `owner` | The owner of the package. |
|
| `owner` | The owner of the package. |
|
||||||
|
| `group` | The package group . |
|
||||||
| `package_name` | The package name. |
|
| `package_name` | The package name. |
|
||||||
| `package_version` | The package version. |
|
| `package_version` | The package version. |
|
||||||
| `architecture` | The package architecture. |
|
| `architecture` | The package architecture. |
|
||||||
@@ -92,7 +95,7 @@ Example request using HTTP Basic authentication:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --user your_username:your_token_or_password -X DELETE \
|
curl --user your_username:your_token_or_password -X DELETE \
|
||||||
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
|
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
|
||||||
```
|
```
|
||||||
|
|
||||||
The server responds with the following HTTP Status codes.
|
The server responds with the following HTTP Status codes.
|
||||||
|
|||||||
@@ -27,17 +27,18 @@ menu:
|
|||||||
要注册RPM注册表,请将 URL 添加到已知 `apt` 源列表中:
|
要注册RPM注册表,请将 URL 添加到已知 `apt` 源列表中:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
|
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
|
||||||
```
|
```
|
||||||
|
|
||||||
| 占位符 | 描述 |
|
| 占位符 | 描述 |
|
||||||
| ------- | -------------- |
|
| ------- |--------------------------------------|
|
||||||
| `owner` | 软件包的所有者 |
|
| `owner` | 软件包的所有者 |
|
||||||
|
| `group` | 任何名称,例如 `centos/7`、`el-7`、`fc38` |
|
||||||
|
|
||||||
如果注册表是私有的,请在URL中提供凭据。您可以使用密码或[个人访问令牌](development/api-usage.md#通过-api-认证):
|
如果注册表是私有的,请在URL中提供凭据。您可以使用密码或[个人访问令牌](development/api-usage.md#通过-api-认证):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
|
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
|
||||||
```
|
```
|
||||||
|
|
||||||
您还必须将凭据添加到 `/etc/yum.repos.d` 中的 `rpm.repo` 文件中的URL中。
|
您还必须将凭据添加到 `/etc/yum.repos.d` 中的 `rpm.repo` 文件中的URL中。
|
||||||
@@ -47,19 +48,20 @@ dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.
|
|||||||
要发布RPM软件包(`*.rpm`),请执行带有软件包内容的 HTTP `PUT` 操作。
|
要发布RPM软件包(`*.rpm`),请执行带有软件包内容的 HTTP `PUT` 操作。
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
|
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
|
||||||
```
|
```
|
||||||
|
|
||||||
| 参数 | 描述 |
|
| 参数 | 描述 |
|
||||||
| ------- |--------------|
|
| ------- |--------------|
|
||||||
| `owner` | 软件包的所有者 |
|
| `owner` | 软件包的所有者 |
|
||||||
|
| `group` | 软件包自定义分组名称 |
|
||||||
|
|
||||||
使用HTTP基本身份验证的示例请求:
|
使用HTTP基本身份验证的示例请求:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --user your_username:your_password_or_token \
|
curl --user your_username:your_password_or_token \
|
||||||
--upload-file path/to/file.rpm \
|
--upload-file path/to/file.rpm \
|
||||||
https://gitea.example.com/api/packages/testuser/rpm/upload
|
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/version/upload
|
||||||
```
|
```
|
||||||
|
|
||||||
如果您使用 2FA 或 OAuth,请使用[个人访问令牌](development/api-usage.md#通过-api-认证)替代密码。您无法将具有相同名称的文件两次发布到软件包中。您必须先删除现有的软件包版本。
|
如果您使用 2FA 或 OAuth,请使用[个人访问令牌](development/api-usage.md#通过-api-认证)替代密码。您无法将具有相同名称的文件两次发布到软件包中。您必须先删除现有的软件包版本。
|
||||||
@@ -77,12 +79,13 @@ curl --user your_username:your_password_or_token \
|
|||||||
要删除 RPM 软件包,请执行 HTTP `DELETE` 操作。如果没有文件剩余,这也将删除软件包版本。
|
要删除 RPM 软件包,请执行 HTTP `DELETE` 操作。如果没有文件剩余,这也将删除软件包版本。
|
||||||
|
|
||||||
```
|
```
|
||||||
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
|
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
|
||||||
```
|
```
|
||||||
|
|
||||||
| 参数 | 描述 |
|
| 参数 | 描述 |
|
||||||
| ----------------- | -------------- |
|
| ----------------- | -------------- |
|
||||||
| `owner` | 软件包的所有者 |
|
| `owner` | 软件包的所有者 |
|
||||||
|
| `group` | 软件包自定义分组 |
|
||||||
| `package_name` | 软件包名称 |
|
| `package_name` | 软件包名称 |
|
||||||
| `package_version` | 软件包版本 |
|
| `package_version` | 软件包版本 |
|
||||||
| `architecture` | 软件包架构 |
|
| `architecture` | 软件包架构 |
|
||||||
@@ -91,7 +94,7 @@ DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{packag
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --user your_username:your_token_or_password -X DELETE \
|
curl --user your_username:your_token_or_password -X DELETE \
|
||||||
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
|
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
|
||||||
```
|
```
|
||||||
|
|
||||||
服务器将以以下HTTP状态码响应:
|
服务器将以以下HTTP状态码响应:
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ require (
|
|||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.6 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
||||||
github.com/couchbase/gomemcached v0.3.0 // indirect
|
github.com/couchbase/gomemcached v0.3.0 // indirect
|
||||||
github.com/couchbase/goutils v0.1.2 // indirect
|
github.com/couchbase/goutils v0.1.2 // indirect
|
||||||
|
|||||||
@@ -215,8 +215,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
|
|||||||
@@ -168,12 +168,13 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow.
|
// CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow.
|
||||||
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error {
|
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
|
||||||
// Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'.
|
// Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'.
|
||||||
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
|
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
WorkflowID: workflowID,
|
WorkflowID: workflowID,
|
||||||
|
TriggerEvent: event,
|
||||||
Status: []Status{StatusRunning, StatusWaiting},
|
Status: []Status{StatusRunning, StatusWaiting},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -71,6 +72,7 @@ type FindRunOptions struct {
|
|||||||
WorkflowID string
|
WorkflowID string
|
||||||
Ref string // the commit/tag/… that caused this workflow
|
Ref string // the commit/tag/… that caused this workflow
|
||||||
TriggerUserID int64
|
TriggerUserID int64
|
||||||
|
TriggerEvent webhook_module.HookEventType
|
||||||
Approved bool // not util.OptionalBool, it works only when it's true
|
Approved bool // not util.OptionalBool, it works only when it's true
|
||||||
Status []Status
|
Status []Status
|
||||||
}
|
}
|
||||||
@@ -98,6 +100,9 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||||||
if opts.Ref != "" {
|
if opts.Ref != "" {
|
||||||
cond = cond.And(builder.Eq{"ref": opts.Ref})
|
cond = cond.And(builder.Eq{"ref": opts.Ref})
|
||||||
}
|
}
|
||||||
|
if opts.TriggerEvent != "" {
|
||||||
|
cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
|
||||||
|
}
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package actions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@@ -118,3 +119,22 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
|||||||
|
|
||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
|
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
|
||||||
|
// There is no other place we can do this because the app.ini will be changed manually
|
||||||
|
if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
|
||||||
|
return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
|
||||||
|
}
|
||||||
|
// cancel running cron jobs of this repository and delete old schedules
|
||||||
|
if err := CancelRunningJobs(
|
||||||
|
ctx,
|
||||||
|
repo.ID,
|
||||||
|
repo.DefaultBranch,
|
||||||
|
"",
|
||||||
|
webhook_module.HookEventSchedule,
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("CancelRunningJobs: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -446,8 +446,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := db.GetEngine(ctx).Where(cond).
|
sess := db.GetEngine(ctx).Where(cond)
|
||||||
Select("`action`.*"). // this line will avoid select other joined table's columns
|
if setting.Database.Type.IsMySQL() {
|
||||||
|
sess = sess.IndexHint("USE", "JOIN", "IDX_action_c_u_d")
|
||||||
|
}
|
||||||
|
sess = sess.Select("`action`.*"). // this line will avoid select other joined table's columns
|
||||||
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
|
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
|
||||||
|
|
||||||
opts.SetDefaultValues()
|
opts.SetDefaultValues()
|
||||||
|
|||||||
+21
-30
@@ -11,21 +11,13 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
"github.com/keybase/go-crypto/openpgp"
|
"github.com/keybase/go-crypto/openpgp"
|
||||||
"github.com/keybase/go-crypto/openpgp/packet"
|
"github.com/keybase/go-crypto/openpgp/packet"
|
||||||
"xorm.io/xorm"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// __________________ ________ ____ __.
|
|
||||||
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
|
|
||||||
// / \ ___ | ___/ \ ___ | <_/ __ < | |
|
|
||||||
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
|
|
||||||
// \______ /|____| \______ / |____|__ \___ > ____|
|
|
||||||
// \/ \/ \/ \/\/
|
|
||||||
|
|
||||||
// GPGKey represents a GPG key.
|
// GPGKey represents a GPG key.
|
||||||
type GPGKey struct {
|
type GPGKey struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
@@ -54,12 +46,11 @@ func (key *GPGKey) BeforeInsert() {
|
|||||||
key.AddedUnix = timeutil.TimeStampNow()
|
key.AddedUnix = timeutil.TimeStampNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
func (key *GPGKey) LoadSubKeys(ctx context.Context) error {
|
||||||
func (key *GPGKey) AfterLoad(session *xorm.Session) {
|
if err := db.GetEngine(ctx).Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey); err != nil {
|
||||||
err := session.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey)
|
return fmt.Errorf("find Sub GPGkeys[%s]: %v", key.KeyID, err)
|
||||||
if err != nil {
|
|
||||||
log.Error("Find Sub GPGkeys[%s]: %v", key.KeyID, err)
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaddedKeyID show KeyID padded to 16 characters
|
// PaddedKeyID show KeyID padded to 16 characters
|
||||||
@@ -76,20 +67,26 @@ func PaddedKeyID(keyID string) string {
|
|||||||
return zeros[0:16-len(keyID)] + keyID
|
return zeros[0:16-len(keyID)] + keyID
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGPGKeys returns a list of public keys belongs to given user.
|
type FindGPGKeyOptions struct {
|
||||||
func ListGPGKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*GPGKey, error) {
|
db.ListOptions
|
||||||
sess := db.GetEngine(ctx).Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid)
|
OwnerID int64
|
||||||
if listOptions.Page != 0 {
|
KeyID string
|
||||||
sess = db.SetSessionPagination(sess, &listOptions)
|
IncludeSubKeys bool
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := make([]*GPGKey, 0, 2)
|
func (opts FindGPGKeyOptions) ToConds() builder.Cond {
|
||||||
return keys, sess.Find(&keys)
|
cond := builder.NewCond()
|
||||||
|
if !opts.IncludeSubKeys {
|
||||||
|
cond = cond.And(builder.Eq{"primary_key_id": ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountUserGPGKeys return number of gpg keys a user own
|
if opts.OwnerID > 0 {
|
||||||
func CountUserGPGKeys(ctx context.Context, userID int64) (int64, error) {
|
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||||
return db.GetEngine(ctx).Where("owner_id=? AND primary_key_id=''", userID).Count(&GPGKey{})
|
}
|
||||||
|
if opts.KeyID != "" {
|
||||||
|
cond = cond.And(builder.Eq{"key_id": opts.KeyID})
|
||||||
|
}
|
||||||
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
|
func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
|
||||||
@@ -103,12 +100,6 @@ func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, e
|
|||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGPGKeysByKeyID returns public key by given ID.
|
|
||||||
func GetGPGKeysByKeyID(ctx context.Context, keyID string) ([]*GPGKey, error) {
|
|
||||||
keys := make([]*GPGKey, 0, 1)
|
|
||||||
return keys, db.GetEngine(ctx).Where("key_id=?", keyID).Find(&keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GPGKeyToEntity retrieve the imported key and the traducted entity
|
// GPGKeyToEntity retrieve the imported key and the traducted entity
|
||||||
func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
|
func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
|
||||||
impKey, err := GetGPGImportByKeyID(ctx, k.KeyID)
|
impKey, err := GetGPGImportByKeyID(ctx, k.KeyID)
|
||||||
|
|||||||
@@ -166,7 +166,9 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
|
|||||||
|
|
||||||
// Now try to associate the signature with the committer, if present
|
// Now try to associate the signature with the committer, if present
|
||||||
if committer.ID != 0 {
|
if committer.ID != 0 {
|
||||||
keys, err := ListGPGKeys(ctx, committer.ID, db.ListOptions{})
|
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||||
|
OwnerID: committer.ID,
|
||||||
|
})
|
||||||
if err != nil { // Skipping failed to get gpg keys of user
|
if err != nil { // Skipping failed to get gpg keys of user
|
||||||
log.Error("ListGPGKeys: %v", err)
|
log.Error("ListGPGKeys: %v", err)
|
||||||
return &CommitVerification{
|
return &CommitVerification{
|
||||||
@@ -176,6 +178,15 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
|
||||||
|
log.Error("LoadSubKeys: %v", err)
|
||||||
|
return &CommitVerification{
|
||||||
|
CommittingUser: committer,
|
||||||
|
Verified: false,
|
||||||
|
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
|
committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
|
||||||
activated := false
|
activated := false
|
||||||
for _, e := range committerEmailAddresses {
|
for _, e := range committerEmailAddresses {
|
||||||
@@ -392,7 +403,10 @@ func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s
|
|||||||
if keyID == "" {
|
if keyID == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
keys, err := GetGPGKeysByKeyID(ctx, keyID)
|
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||||
|
KeyID: keyID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetGPGKeysByKeyID: %v", err)
|
log.Error("GetGPGKeysByKeyID: %v", err)
|
||||||
return &CommitVerification{
|
return &CommitVerification{
|
||||||
@@ -407,7 +421,10 @@ func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s
|
|||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
var primaryKeys []*GPGKey
|
var primaryKeys []*GPGKey
|
||||||
if key.PrimaryKeyID != "" {
|
if key.PrimaryKeyID != "" {
|
||||||
primaryKeys, err = GetGPGKeysByKeyID(ctx, key.PrimaryKeyID)
|
primaryKeys, err = db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||||
|
KeyID: key.PrimaryKeyID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetGPGKeysByKeyID: %v", err)
|
log.Error("GetGPGKeysByKeyID: %v", err)
|
||||||
return &CommitVerification{
|
return &CommitVerification{
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package asymkey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GPGKeyList []*GPGKey
|
||||||
|
|
||||||
|
func (keys GPGKeyList) keyIDs() []string {
|
||||||
|
ids := make([]string, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
ids[i] = key.KeyID
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keys GPGKeyList) LoadSubKeys(ctx context.Context) error {
|
||||||
|
subKeys := make([]*GPGKey, 0, len(keys))
|
||||||
|
if err := db.GetEngine(ctx).In("primary_key_id", keys.keyIDs()).Find(&subKeys); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
subKeysMap := make(map[string][]*GPGKey, len(subKeys))
|
||||||
|
for _, key := range subKeys {
|
||||||
|
subKeysMap[key.PrimaryKeyID] = append(subKeysMap[key.PrimaryKeyID], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if subKeys, ok := subKeysMap[key.KeyID]; ok {
|
||||||
|
key.SubsKey = subKeys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -197,10 +197,10 @@ func (opts FindPublicKeyOptions) ToConds() builder.Cond {
|
|||||||
cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
|
cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
|
||||||
}
|
}
|
||||||
if len(opts.KeyTypes) > 0 {
|
if len(opts.KeyTypes) > 0 {
|
||||||
cond = cond.And(builder.In("type", opts.KeyTypes))
|
cond = cond.And(builder.In("`type`", opts.KeyTypes))
|
||||||
}
|
}
|
||||||
if opts.NotKeytype > 0 {
|
if opts.NotKeytype > 0 {
|
||||||
cond = cond.And(builder.Neq{"type": opts.NotKeytype})
|
cond = cond.And(builder.Neq{"`type`": opts.NotKeytype})
|
||||||
}
|
}
|
||||||
if opts.LoginSourceID > 0 {
|
if opts.LoginSourceID > 0 {
|
||||||
cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
|
cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
|
||||||
|
|||||||
@@ -15,15 +15,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// __________ .__ .__ .__
|
|
||||||
// \______ _______|__| ____ ____ |_____________ | | ______
|
|
||||||
// | ___\_ __ | |/ \_/ ___\| \____ \__ \ | | / ___/
|
|
||||||
// | | | | \| | | \ \___| | |_> / __ \| |__\___ \
|
|
||||||
// |____| |__| |__|___| /\___ |__| __(____ |____/____ >
|
|
||||||
// \/ \/ |__| \/ \/
|
|
||||||
//
|
|
||||||
// This file contains functions related to principals
|
|
||||||
|
|
||||||
// AddPrincipalKey adds new principal to database and authorized_principals file.
|
// AddPrincipalKey adds new principal to database and authorized_principals file.
|
||||||
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
|
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
dbCtx, committer, err := db.TxContext(ctx)
|
||||||
@@ -103,17 +94,3 @@ func CheckPrincipalKeyString(ctx context.Context, user *user_model.User, content
|
|||||||
|
|
||||||
return "", fmt.Errorf("didn't match allowed principals: %s", setting.SSH.AuthorizedPrincipalsAllow)
|
return "", fmt.Errorf("didn't match allowed principals: %s", setting.SSH.AuthorizedPrincipalsAllow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPrincipalKeys returns a list of principals belongs to given user.
|
|
||||||
func ListPrincipalKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*PublicKey, error) {
|
|
||||||
sess := db.GetEngine(ctx).Where("owner_id = ? AND type = ?", uid, KeyTypePrincipal)
|
|
||||||
if listOptions.Page != 0 {
|
|
||||||
sess = db.SetSessionPagination(sess, &listOptions)
|
|
||||||
|
|
||||||
keys := make([]*PublicKey, 0, listOptions.PageSize)
|
|
||||||
return keys, sess.Find(&keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := make([]*PublicKey, 0, 5)
|
|
||||||
return keys, sess.Find(&keys)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
|
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
|
||||||
@@ -83,7 +82,7 @@ func (cred *WebAuthnCredential) BeforeUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
||||||
func (cred *WebAuthnCredential) AfterLoad(session *xorm.Session) {
|
func (cred *WebAuthnCredential) AfterLoad() {
|
||||||
cred.LowerName = strings.ToLower(cred.Name)
|
cred.LowerName = strings.ToLower(cred.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-30
@@ -21,17 +21,9 @@ const (
|
|||||||
// Paginator is the base for different ListOptions types
|
// Paginator is the base for different ListOptions types
|
||||||
type Paginator interface {
|
type Paginator interface {
|
||||||
GetSkipTake() (skip, take int)
|
GetSkipTake() (skip, take int)
|
||||||
GetStartEnd() (start, end int)
|
|
||||||
IsListAll() bool
|
IsListAll() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPaginatedSession creates a paginated database session
|
|
||||||
func GetPaginatedSession(p Paginator) *xorm.Session {
|
|
||||||
skip, take := p.GetSkipTake()
|
|
||||||
|
|
||||||
return x.Limit(take, skip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSessionPagination sets pagination for a database session
|
// SetSessionPagination sets pagination for a database session
|
||||||
func SetSessionPagination(sess Engine, p Paginator) *xorm.Session {
|
func SetSessionPagination(sess Engine, p Paginator) *xorm.Session {
|
||||||
skip, take := p.GetSkipTake()
|
skip, take := p.GetSkipTake()
|
||||||
@@ -39,13 +31,6 @@ func SetSessionPagination(sess Engine, p Paginator) *xorm.Session {
|
|||||||
return sess.Limit(take, skip)
|
return sess.Limit(take, skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEnginePagination sets pagination for a database engine
|
|
||||||
func SetEnginePagination(e Engine, p Paginator) Engine {
|
|
||||||
skip, take := p.GetSkipTake()
|
|
||||||
|
|
||||||
return e.Limit(take, skip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptions options to paginate results
|
// ListOptions options to paginate results
|
||||||
type ListOptions struct {
|
type ListOptions struct {
|
||||||
PageSize int
|
PageSize int
|
||||||
@@ -66,13 +51,6 @@ func (opts *ListOptions) GetSkipTake() (skip, take int) {
|
|||||||
return (opts.Page - 1) * opts.PageSize, opts.PageSize
|
return (opts.Page - 1) * opts.PageSize, opts.PageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStartEnd returns the start and end of the ListOptions
|
|
||||||
func (opts *ListOptions) GetStartEnd() (start, end int) {
|
|
||||||
start, take := opts.GetSkipTake()
|
|
||||||
end = start + take
|
|
||||||
return start, end
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts ListOptions) GetPage() int {
|
func (opts ListOptions) GetPage() int {
|
||||||
return opts.Page
|
return opts.Page
|
||||||
}
|
}
|
||||||
@@ -135,11 +113,6 @@ func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) {
|
|||||||
return opts.skip, opts.take
|
return opts.skip, opts.take
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStartEnd returns the start and end values
|
|
||||||
func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) {
|
|
||||||
return opts.skip, opts.skip + opts.take
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindOptions represents a find options
|
// FindOptions represents a find options
|
||||||
type FindOptions interface {
|
type FindOptions interface {
|
||||||
GetPage() int
|
GetPage() int
|
||||||
@@ -148,15 +121,34 @@ type FindOptions interface {
|
|||||||
ToConds() builder.Cond
|
ToConds() builder.Cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JoinFunc func(sess Engine) error
|
||||||
|
|
||||||
|
type FindOptionsJoin interface {
|
||||||
|
ToJoins() []JoinFunc
|
||||||
|
}
|
||||||
|
|
||||||
type FindOptionsOrder interface {
|
type FindOptionsOrder interface {
|
||||||
ToOrders() string
|
ToOrders() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find represents a common find function which accept an options interface
|
// Find represents a common find function which accept an options interface
|
||||||
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||||
sess := GetEngine(ctx).Where(opts.ToConds())
|
sess := GetEngine(ctx)
|
||||||
|
|
||||||
|
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
|
||||||
|
for _, joinFunc := range joinOpt.ToJoins() {
|
||||||
|
if err := joinFunc(sess); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sess = sess.Where(opts.ToConds())
|
||||||
page, pageSize := opts.GetPage(), opts.GetPageSize()
|
page, pageSize := opts.GetPage(), opts.GetPageSize()
|
||||||
if !opts.IsListAll() && pageSize > 0 && page >= 1 {
|
if !opts.IsListAll() && pageSize > 0 {
|
||||||
|
if page == 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
sess.Limit(pageSize, (page-1)*pageSize)
|
sess.Limit(pageSize, (page-1)*pageSize)
|
||||||
}
|
}
|
||||||
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
|
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
|
||||||
@@ -176,8 +168,17 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
|||||||
|
|
||||||
// Count represents a common count function which accept an options interface
|
// Count represents a common count function which accept an options interface
|
||||||
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
||||||
|
sess := GetEngine(ctx)
|
||||||
|
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
|
||||||
|
for _, joinFunc := range joinOpt.ToJoins() {
|
||||||
|
if err := joinFunc(sess); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var object T
|
var object T
|
||||||
return GetEngine(ctx).Where(opts.ToConds()).Count(&object)
|
return sess.Where(opts.ToConds()).Count(&object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindAndCount represents a common findandcount function which accept an options interface
|
// FindAndCount represents a common findandcount function which accept an options interface
|
||||||
|
|||||||
@@ -52,11 +52,8 @@ func TestPaginator(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
skip, take := c.Paginator.GetSkipTake()
|
skip, take := c.Paginator.GetSkipTake()
|
||||||
start, end := c.Paginator.GetStartEnd()
|
|
||||||
|
|
||||||
assert.Equal(t, c.Skip, skip)
|
assert.Equal(t, c.Skip, skip)
|
||||||
assert.Equal(t, c.Take, take)
|
assert.Equal(t, c.Take, take)
|
||||||
assert.Equal(t, c.Start, start)
|
|
||||||
assert.Equal(t, c.End, end)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,21 @@ func (err ErrUserOwnPackages) Error() string {
|
|||||||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
||||||
|
type ErrDeleteLastAdminUser struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
||||||
|
func IsErrDeleteLastAdminUser(err error) bool {
|
||||||
|
_, ok := err.(ErrDeleteLastAdminUser)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrDeleteLastAdminUser) Error() string {
|
||||||
|
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
||||||
// transfer request
|
// transfer request
|
||||||
type ErrNoPendingRepoTransfer struct {
|
type ErrNoPendingRepoTransfer struct {
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RenameBranch rename a branch
|
// RenameBranch rename a branch
|
||||||
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
|
func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(ctx context.Context, isDefault bool) error) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -358,7 +358,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. do git action
|
// 5. do git action
|
||||||
if err = gitAction(isDefault); err != nil {
|
if err = gitAction(ctx, isDefault); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package git_test
|
package git_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@@ -132,7 +133,7 @@ func TestRenameBranch(t *testing.T) {
|
|||||||
}, git_model.WhitelistOptions{}))
|
}, git_model.WhitelistOptions{}))
|
||||||
assert.NoError(t, committer.Commit())
|
assert.NoError(t, committer.Commit())
|
||||||
|
|
||||||
assert.NoError(t, git_model.RenameBranch(db.DefaultContext, repo1, "master", "main", func(isDefault bool) error {
|
assert.NoError(t, git_model.RenameBranch(db.DefaultContext, repo1, "master", "main", func(ctx context.Context, isDefault bool) error {
|
||||||
_isDefault = isDefault
|
_isDefault = isDefault
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ type ProtectedBranch struct {
|
|||||||
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
|
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
|
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
|
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
|
IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
|
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
ProtectedFilePatterns string `xorm:"TEXT"`
|
ProtectedFilePatterns string `xorm:"TEXT"`
|
||||||
UnprotectedFilePatterns string `xorm:"TEXT"`
|
UnprotectedFilePatterns string `xorm:"TEXT"`
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrCommentNotExist represents a "CommentNotExist" kind of error.
|
// ErrCommentNotExist represents a "CommentNotExist" kind of error.
|
||||||
@@ -338,7 +337,7 @@ func (c *Comment) BeforeUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
||||||
func (c *Comment) AfterLoad(session *xorm.Session) {
|
func (c *Comment) AfterLoad() {
|
||||||
c.Patch = c.PatchQuoted
|
c.Patch = c.PatchQuoted
|
||||||
if len(c.PatchQuoted) > 0 && c.PatchQuoted[0] == '"' {
|
if len(c.PatchQuoted) > 0 && c.PatchQuoted[0] == '"' {
|
||||||
unquoted, err := strconv.Unquote(c.PatchQuoted)
|
unquoted, err := strconv.Unquote(c.PatchQuoted)
|
||||||
|
|||||||
@@ -801,7 +801,7 @@ func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.Prot
|
|||||||
And("type = ?", ReviewTypeApprove).
|
And("type = ?", ReviewTypeApprove).
|
||||||
And("official = ?", true).
|
And("official = ?", true).
|
||||||
And("dismissed = ?", false)
|
And("dismissed = ?", false)
|
||||||
if protectBranch.DismissStaleApprovals {
|
if protectBranch.IgnoreStaleApprovals {
|
||||||
sess = sess.And("stale = ?", false)
|
sess = sess.And("stale = ?", false)
|
||||||
}
|
}
|
||||||
approvals, err := sess.Count(new(Review))
|
approvals, err := sess.Count(new(Review))
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ type FindTrackedTimesOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// toCond will convert each condition into a xorm-Cond
|
// toCond will convert each condition into a xorm-Cond
|
||||||
func (opts *FindTrackedTimesOptions) toCond() builder.Cond {
|
func (opts *FindTrackedTimesOptions) ToConds() builder.Cond {
|
||||||
cond := builder.NewCond().And(builder.Eq{"tracked_time.deleted": false})
|
cond := builder.NewCond().And(builder.Eq{"tracked_time.deleted": false})
|
||||||
if opts.IssueID != 0 {
|
if opts.IssueID != 0 {
|
||||||
cond = cond.And(builder.Eq{"issue_id": opts.IssueID})
|
cond = cond.And(builder.Eq{"issue_id": opts.IssueID})
|
||||||
@@ -117,6 +117,18 @@ func (opts *FindTrackedTimesOptions) toCond() builder.Cond {
|
|||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opts *FindTrackedTimesOptions) ToJoins() []db.JoinFunc {
|
||||||
|
if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
|
||||||
|
return []db.JoinFunc{
|
||||||
|
func(e db.Engine) error {
|
||||||
|
e.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// toSession will convert the given options to a xorm Session by using the conditions from toCond and joining with issue table if required
|
// toSession will convert the given options to a xorm Session by using the conditions from toCond and joining with issue table if required
|
||||||
func (opts *FindTrackedTimesOptions) toSession(e db.Engine) db.Engine {
|
func (opts *FindTrackedTimesOptions) toSession(e db.Engine) db.Engine {
|
||||||
sess := e
|
sess := e
|
||||||
@@ -124,10 +136,10 @@ func (opts *FindTrackedTimesOptions) toSession(e db.Engine) db.Engine {
|
|||||||
sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
|
sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
sess = sess.Where(opts.toCond())
|
sess = sess.Where(opts.ToConds())
|
||||||
|
|
||||||
if opts.Page != 0 {
|
if opts.Page != 0 {
|
||||||
sess = db.SetEnginePagination(sess, opts)
|
sess = db.SetSessionPagination(sess, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sess
|
return sess
|
||||||
@@ -141,7 +153,7 @@ func GetTrackedTimes(ctx context.Context, options *FindTrackedTimesOptions) (tra
|
|||||||
|
|
||||||
// CountTrackedTimes returns count of tracked times that fit to the given options.
|
// CountTrackedTimes returns count of tracked times that fit to the given options.
|
||||||
func CountTrackedTimes(ctx context.Context, opts *FindTrackedTimesOptions) (int64, error) {
|
func CountTrackedTimes(ctx context.Context, opts *FindTrackedTimesOptions) (int64, error) {
|
||||||
sess := db.GetEngine(ctx).Where(opts.toCond())
|
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||||
if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
|
if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
|
||||||
sess = sess.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
|
sess = sess.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_22 //nolint
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddIgnoreStaleApprovalsColumnToProtectedBranchTable(x *xorm.Engine) error {
|
||||||
|
type ProtectedBranch struct {
|
||||||
|
IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
|
}
|
||||||
|
return x.Sync(new(ProtectedBranch))
|
||||||
|
}
|
||||||
+3
-10
@@ -111,7 +111,7 @@ type FindRepoArchiversOption struct {
|
|||||||
OlderThan time.Duration
|
OlderThan time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts FindRepoArchiversOption) toConds() builder.Cond {
|
func (opts FindRepoArchiversOption) ToConds() builder.Cond {
|
||||||
cond := builder.NewCond()
|
cond := builder.NewCond()
|
||||||
if opts.OlderThan > 0 {
|
if opts.OlderThan > 0 {
|
||||||
cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-opts.OlderThan).Unix()})
|
cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-opts.OlderThan).Unix()})
|
||||||
@@ -119,15 +119,8 @@ func (opts FindRepoArchiversOption) toConds() builder.Cond {
|
|||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindRepoArchives find repo archivers
|
func (opts FindRepoArchiversOption) ToOrders() string {
|
||||||
func FindRepoArchives(ctx context.Context, opts FindRepoArchiversOption) ([]*RepoArchiver, error) {
|
return "created_unix ASC"
|
||||||
archivers := make([]*RepoArchiver, 0, opts.PageSize)
|
|
||||||
start, limit := opts.GetSkipTake()
|
|
||||||
err := db.GetEngine(ctx).Where(opts.toConds()).
|
|
||||||
Asc("created_unix").
|
|
||||||
Limit(limit, start).
|
|
||||||
Find(&archivers)
|
|
||||||
return archivers, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetArchiveRepoState sets if a repo is archived
|
// SetArchiveRepoState sets if a repo is archived
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import (
|
|||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collaboration represent the relation between an individual and a repository.
|
// Collaboration represent the relation between an individual and a repository.
|
||||||
@@ -37,35 +38,38 @@ type Collaborator struct {
|
|||||||
|
|
||||||
// GetCollaborators returns the collaborators for a repository
|
// GetCollaborators returns the collaborators for a repository
|
||||||
func GetCollaborators(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*Collaborator, error) {
|
func GetCollaborators(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*Collaborator, error) {
|
||||||
collaborations, err := getCollaborations(ctx, repoID, listOptions)
|
collaborations, err := db.Find[Collaboration](ctx, FindCollaborationOptions{
|
||||||
|
ListOptions: listOptions,
|
||||||
|
RepoID: repoID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getCollaborations: %w", err)
|
return nil, fmt.Errorf("db.Find[Collaboration]: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
collaborators := make([]*Collaborator, 0, len(collaborations))
|
collaborators := make([]*Collaborator, 0, len(collaborations))
|
||||||
|
userIDs := make([]int64, 0, len(collaborations))
|
||||||
for _, c := range collaborations {
|
for _, c := range collaborations {
|
||||||
user, err := user_model.GetUserByID(ctx, c.UserID)
|
userIDs = append(userIDs, c.UserID)
|
||||||
if err != nil {
|
|
||||||
if user_model.IsErrUserNotExist(err) {
|
|
||||||
log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repoID)
|
|
||||||
user = user_model.NewGhostUser()
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usersMap := make(map[int64]*user_model.User)
|
||||||
|
if err := db.GetEngine(ctx).In("id", userIDs).Find(&usersMap); err != nil {
|
||||||
|
return nil, fmt.Errorf("Find users map by user ids: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range collaborations {
|
||||||
|
u := usersMap[c.UserID]
|
||||||
|
if u == nil {
|
||||||
|
u = user_model.NewGhostUser()
|
||||||
}
|
}
|
||||||
collaborators = append(collaborators, &Collaborator{
|
collaborators = append(collaborators, &Collaborator{
|
||||||
User: user,
|
User: u,
|
||||||
Collaboration: c,
|
Collaboration: c,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return collaborators, nil
|
return collaborators, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountCollaborators returns total number of collaborators for a repository
|
|
||||||
func CountCollaborators(ctx context.Context, repoID int64) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where("repo_id = ? ", repoID).Count(&Collaboration{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCollaboration get collaboration for a repository id with a user id
|
// GetCollaboration get collaboration for a repository id with a user id
|
||||||
func GetCollaboration(ctx context.Context, repoID, uid int64) (*Collaboration, error) {
|
func GetCollaboration(ctx context.Context, repoID, uid int64) (*Collaboration, error) {
|
||||||
collaboration := &Collaboration{
|
collaboration := &Collaboration{
|
||||||
@@ -84,18 +88,13 @@ func IsCollaborator(ctx context.Context, repoID, userID int64) (bool, error) {
|
|||||||
return db.GetEngine(ctx).Get(&Collaboration{RepoID: repoID, UserID: userID})
|
return db.GetEngine(ctx).Get(&Collaboration{RepoID: repoID, UserID: userID})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCollaborations(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*Collaboration, error) {
|
type FindCollaborationOptions struct {
|
||||||
if listOptions.Page == 0 {
|
db.ListOptions
|
||||||
collaborations := make([]*Collaboration, 0, 8)
|
RepoID int64
|
||||||
return collaborations, db.GetEngine(ctx).Find(&collaborations, &Collaboration{RepoID: repoID})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
func (opts FindCollaborationOptions) ToConds() builder.Cond {
|
||||||
|
return builder.And(builder.Eq{"repo_id": opts.RepoID})
|
||||||
e = db.SetEnginePagination(e, &listOptions)
|
|
||||||
|
|
||||||
collaborations := make([]*Collaboration, 0, listOptions.PageSize)
|
|
||||||
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repoID})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
||||||
|
|||||||
@@ -89,17 +89,23 @@ func TestRepository_CountCollaborators(t *testing.T) {
|
|||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
count, err := repo_model.CountCollaborators(db.DefaultContext, repo1.ID)
|
count, err := db.Count[repo_model.Collaboration](db.DefaultContext, repo_model.FindCollaborationOptions{
|
||||||
|
RepoID: repo1.ID,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 2, count)
|
assert.EqualValues(t, 2, count)
|
||||||
|
|
||||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
|
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22})
|
||||||
count, err = repo_model.CountCollaborators(db.DefaultContext, repo2.ID)
|
count, err = db.Count[repo_model.Collaboration](db.DefaultContext, repo_model.FindCollaborationOptions{
|
||||||
|
RepoID: repo2.ID,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 2, count)
|
assert.EqualValues(t, 2, count)
|
||||||
|
|
||||||
// Non-existent repository.
|
// Non-existent repository.
|
||||||
count, err = repo_model.CountCollaborators(db.DefaultContext, unittest.NonexistentID)
|
count, err = db.Count[repo_model.Collaboration](db.DefaultContext, repo_model.FindCollaborationOptions{
|
||||||
|
RepoID: unittest.NonexistentID,
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 0, count)
|
assert.EqualValues(t, 0, count)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -56,13 +56,16 @@ func GetUserFork(ctx context.Context, repoID, userID int64) (*Repository, error)
|
|||||||
|
|
||||||
// GetForks returns all the forks of the repository
|
// GetForks returns all the forks of the repository
|
||||||
func GetForks(ctx context.Context, repo *Repository, listOptions db.ListOptions) ([]*Repository, error) {
|
func GetForks(ctx context.Context, repo *Repository, listOptions db.ListOptions) ([]*Repository, error) {
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
var forks []*Repository
|
||||||
if listOptions.Page == 0 {
|
if listOptions.Page == 0 {
|
||||||
forks := make([]*Repository, 0, repo.NumForks)
|
forks = make([]*Repository, 0, repo.NumForks)
|
||||||
return forks, db.GetEngine(ctx).Find(&forks, &Repository{ForkID: repo.ID})
|
} else {
|
||||||
|
forks = make([]*Repository, 0, listOptions.PageSize)
|
||||||
|
sess = db.SetSessionPagination(sess, &listOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := db.GetPaginatedSession(&listOptions)
|
|
||||||
forks := make([]*Repository, 0, listOptions.PageSize)
|
|
||||||
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
|
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-35
@@ -225,6 +225,7 @@ func GetReleaseForRepoByID(ctx context.Context, repoID, id int64) (*Release, err
|
|||||||
// FindReleasesOptions describes the conditions to Find releases
|
// FindReleasesOptions describes the conditions to Find releases
|
||||||
type FindReleasesOptions struct {
|
type FindReleasesOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
|
RepoID int64
|
||||||
IncludeDrafts bool
|
IncludeDrafts bool
|
||||||
IncludeTags bool
|
IncludeTags bool
|
||||||
IsPreRelease util.OptionalBool
|
IsPreRelease util.OptionalBool
|
||||||
@@ -233,9 +234,8 @@ type FindReleasesOptions struct {
|
|||||||
HasSha1 util.OptionalBool // useful to find draft releases which are created with existing tags
|
HasSha1 util.OptionalBool // useful to find draft releases which are created with existing tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
|
func (opts FindReleasesOptions) ToConds() builder.Cond {
|
||||||
cond := builder.NewCond()
|
var cond builder.Cond = builder.Eq{"repo_id": opts.RepoID}
|
||||||
cond = cond.And(builder.Eq{"repo_id": repoID})
|
|
||||||
|
|
||||||
if !opts.IncludeDrafts {
|
if !opts.IncludeDrafts {
|
||||||
cond = cond.And(builder.Eq{"is_draft": false})
|
cond = cond.And(builder.Eq{"is_draft": false})
|
||||||
@@ -262,18 +262,8 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
|
|||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoID returns a list of releases of repository.
|
func (opts FindReleasesOptions) ToOrders() string {
|
||||||
func GetReleasesByRepoID(ctx context.Context, repoID int64, opts FindReleasesOptions) ([]*Release, error) {
|
return "created_unix DESC, id DESC"
|
||||||
sess := db.GetEngine(ctx).
|
|
||||||
Desc("created_unix", "id").
|
|
||||||
Where(opts.toConds(repoID))
|
|
||||||
|
|
||||||
if opts.PageSize != 0 {
|
|
||||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
rels := make([]*Release, 0, opts.PageSize)
|
|
||||||
return rels, sess.Find(&rels)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTagNamesByRepoID returns a list of release tag names of repository.
|
// GetTagNamesByRepoID returns a list of release tag names of repository.
|
||||||
@@ -286,23 +276,19 @@ func GetTagNamesByRepoID(ctx context.Context, repoID int64) ([]string, error) {
|
|||||||
IncludeDrafts: true,
|
IncludeDrafts: true,
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
HasSha1: util.OptionalBoolTrue,
|
HasSha1: util.OptionalBoolTrue,
|
||||||
|
RepoID: repoID,
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := make([]string, 0)
|
tags := make([]string, 0)
|
||||||
sess := db.GetEngine(ctx).
|
sess := db.GetEngine(ctx).
|
||||||
Table("release").
|
Table("release").
|
||||||
Desc("created_unix", "id").
|
Desc("created_unix", "id").
|
||||||
Where(opts.toConds(repoID)).
|
Where(opts.ToConds()).
|
||||||
Cols("tag_name")
|
Cols("tag_name")
|
||||||
|
|
||||||
return tags, sess.Find(&tags)
|
return tags, sess.Find(&tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountReleasesByRepoID returns a number of releases matching FindReleaseOptions and RepoID.
|
|
||||||
func CountReleasesByRepoID(ctx context.Context, repoID int64, opts FindReleasesOptions) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where(opts.toConds(repoID)).Count(new(Release))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLatestReleaseByRepoID returns the latest release for a repository
|
// GetLatestReleaseByRepoID returns the latest release for a repository
|
||||||
func GetLatestReleaseByRepoID(ctx context.Context, repoID int64) (*Release, error) {
|
func GetLatestReleaseByRepoID(ctx context.Context, repoID int64) (*Release, error) {
|
||||||
cond := builder.NewCond().
|
cond := builder.NewCond().
|
||||||
@@ -325,20 +311,6 @@ func GetLatestReleaseByRepoID(ctx context.Context, repoID int64) (*Release, erro
|
|||||||
return rel, nil
|
return rel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
|
|
||||||
func GetReleasesByRepoIDAndNames(ctx context.Context, repoID int64, tagNames []string) (rels []*Release, err error) {
|
|
||||||
err = db.GetEngine(ctx).
|
|
||||||
In("tag_name", tagNames).
|
|
||||||
Desc("created_unix").
|
|
||||||
Find(&rels, Release{RepoID: repoID})
|
|
||||||
return rels, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetReleaseCountByRepoID returns the count of releases of repository
|
|
||||||
func GetReleaseCountByRepoID(ctx context.Context, repoID int64, opts FindReleasesOptions) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where(opts.toConds(repoID)).Count(&Release{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type releaseMetaSearch struct {
|
type releaseMetaSearch struct {
|
||||||
ID []int64
|
ID []int64
|
||||||
Rel []*Release
|
Rel []*Release
|
||||||
|
|||||||
@@ -283,29 +283,3 @@ func UpdateRepoUnit(ctx context.Context, unit *RepoUnit) error {
|
|||||||
_, err := db.GetEngine(ctx).ID(unit.ID).Update(unit)
|
_, err := db.GetEngine(ctx).ID(unit.ID).Update(unit)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepositoryUnits updates a repository's units
|
|
||||||
func UpdateRepositoryUnits(ctx context.Context, repo *Repository, units []RepoUnit, deleteUnitTypes []unit.Type) (err error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Delete existing settings of units before adding again
|
|
||||||
for _, u := range units {
|
|
||||||
deleteUnitTypes = append(deleteUnitTypes, u.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(RepoUnit)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(units) > 0 {
|
|
||||||
if err = db.Insert(ctx, units); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|||||||
+27
-4
@@ -730,9 +730,18 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve
|
|||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLastAdminUser check whether user is the last admin
|
||||||
|
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
||||||
|
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// CountUserFilter represent optional filters for CountUsers
|
// CountUserFilter represent optional filters for CountUsers
|
||||||
type CountUserFilter struct {
|
type CountUserFilter struct {
|
||||||
LastLoginSince *int64
|
LastLoginSince *int64
|
||||||
|
IsAdmin util.OptionalBool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountUsers returns number of users.
|
// CountUsers returns number of users.
|
||||||
@@ -741,13 +750,25 @@ func CountUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
||||||
sess := db.GetEngine(ctx).Where(builder.Eq{"type": "0"})
|
sess := db.GetEngine(ctx)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
cond = cond.And(builder.Eq{"type": UserTypeIndividual})
|
||||||
|
|
||||||
if opts != nil && opts.LastLoginSince != nil {
|
if opts != nil {
|
||||||
sess = sess.Where(builder.Gte{"last_login_unix": *opts.LastLoginSince})
|
if opts.LastLoginSince != nil {
|
||||||
|
cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.IsAdmin.IsNone() {
|
||||||
|
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := sess.Where(cond).Count(new(User))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("user.countUsers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, _ := sess.Count(new(User))
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,6 +1251,8 @@ func isUserVisibleToViewerCond(viewer *User) builder.Cond {
|
|||||||
return builder.Neq{
|
return builder.Neq{
|
||||||
"`user`.visibility": structs.VisibleTypePrivate,
|
"`user`.visibility": structs.VisibleTypePrivate,
|
||||||
}.Or(
|
}.Or(
|
||||||
|
// viewer self
|
||||||
|
builder.Eq{"`user`.id": viewer.ID},
|
||||||
// viewer's following
|
// viewer's following
|
||||||
builder.In("`user`.id",
|
builder.In("`user`.id",
|
||||||
builder.
|
builder.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const (
|
|||||||
GithubEventRelease = "release"
|
GithubEventRelease = "release"
|
||||||
GithubEventPullRequestComment = "pull_request_comment"
|
GithubEventPullRequestComment = "pull_request_comment"
|
||||||
GithubEventGollum = "gollum"
|
GithubEventGollum = "gollum"
|
||||||
|
GithubEventSchedule = "schedule"
|
||||||
)
|
)
|
||||||
|
|
||||||
// canGithubEventMatch check if the input Github event can match any Gitea event.
|
// canGithubEventMatch check if the input Github event can match any Gitea event.
|
||||||
@@ -69,6 +70,9 @@ func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEvent
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case GithubEventSchedule:
|
||||||
|
return triggedEvent == webhook_module.HookEventSchedule
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return eventName == string(triggedEvent)
|
return eventName == string(triggedEvent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
type DetectedWorkflow struct {
|
type DetectedWorkflow struct {
|
||||||
EntryName string
|
EntryName string
|
||||||
TriggerEvent string
|
TriggerEvent *jobparser.Event
|
||||||
Content []byte
|
Content []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +100,7 @@ func DetectWorkflows(
|
|||||||
commit *git.Commit,
|
commit *git.Commit,
|
||||||
triggedEvent webhook_module.HookEventType,
|
triggedEvent webhook_module.HookEventType,
|
||||||
payload api.Payloader,
|
payload api.Payloader,
|
||||||
|
detectSchedule bool,
|
||||||
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
||||||
entries, err := ListWorkflows(commit)
|
entries, err := ListWorkflows(commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,6 +115,7 @@ func DetectWorkflows(
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// one workflow may have multiple events
|
||||||
events, err := GetEventsFromContent(content)
|
events, err := GetEventsFromContent(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
|
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
|
||||||
@@ -122,17 +124,18 @@ func DetectWorkflows(
|
|||||||
for _, evt := range events {
|
for _, evt := range events {
|
||||||
log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent)
|
log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent)
|
||||||
if evt.IsSchedule() {
|
if evt.IsSchedule() {
|
||||||
|
if detectSchedule {
|
||||||
dwf := &DetectedWorkflow{
|
dwf := &DetectedWorkflow{
|
||||||
EntryName: entry.Name(),
|
EntryName: entry.Name(),
|
||||||
TriggerEvent: evt.Name,
|
TriggerEvent: evt,
|
||||||
Content: content,
|
Content: content,
|
||||||
}
|
}
|
||||||
schedules = append(schedules, dwf)
|
schedules = append(schedules, dwf)
|
||||||
}
|
}
|
||||||
if detectMatched(gitRepo, commit, triggedEvent, payload, evt) {
|
} else if detectMatched(gitRepo, commit, triggedEvent, payload, evt) {
|
||||||
dwf := &DetectedWorkflow{
|
dwf := &DetectedWorkflow{
|
||||||
EntryName: entry.Name(),
|
EntryName: entry.Name(),
|
||||||
TriggerEvent: evt.Name,
|
TriggerEvent: evt,
|
||||||
Content: content,
|
Content: content,
|
||||||
}
|
}
|
||||||
workflows = append(workflows, dwf)
|
workflows = append(workflows, dwf)
|
||||||
@@ -153,7 +156,8 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web
|
|||||||
webhook_module.HookEventCreate,
|
webhook_module.HookEventCreate,
|
||||||
webhook_module.HookEventDelete,
|
webhook_module.HookEventDelete,
|
||||||
webhook_module.HookEventFork,
|
webhook_module.HookEventFork,
|
||||||
webhook_module.HookEventWiki:
|
webhook_module.HookEventWiki,
|
||||||
|
webhook_module.HookEventSchedule:
|
||||||
if len(evt.Acts()) != 0 {
|
if len(evt.Acts()) != 0 {
|
||||||
log.Warn("Ignore unsupported %s event arguments %v", triggedEvent, evt.Acts())
|
log.Warn("Ignore unsupported %s event arguments %v", triggedEvent, evt.Acts())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,13 @@ func TestDetectMatched(t *testing.T) {
|
|||||||
yamlOn: "on: gollum",
|
yamlOn: "on: gollum",
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "HookEventSchedue(schedule) matches GithubEventSchedule(schedule)",
|
||||||
|
triggedEvent: webhook_module.HookEventSchedule,
|
||||||
|
payload: nil,
|
||||||
|
yamlOn: "on: schedule",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|||||||
@@ -536,18 +536,20 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
|||||||
ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
|
ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["NumTags"], err = repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
|
ctx.Data["NumTags"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
IncludeDrafts: true,
|
IncludeDrafts: true,
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
|
HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
|
ctx.Data["NumReleases"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
||||||
IncludeDrafts: ctx.Repo.CanWrite(unit_model.TypeReleases),
|
IncludeDrafts: ctx.Repo.CanWrite(unit_model.TypeReleases),
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
PropertyMetadata = "rpm.metadata"
|
PropertyMetadata = "rpm.metadata"
|
||||||
|
|
||||||
SettingKeyPrivate = "rpm.key.private"
|
SettingKeyPrivate = "rpm.key.private"
|
||||||
SettingKeyPublic = "rpm.key.public"
|
SettingKeyPublic = "rpm.key.public"
|
||||||
|
|
||||||
|
|||||||
@@ -299,10 +299,11 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR
|
|||||||
IncludeDrafts: true,
|
IncludeDrafts: true,
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
ListOptions: db.ListOptions{PageSize: 50},
|
ListOptions: db.ListOptions{PageSize: 50},
|
||||||
|
RepoID: repo.ID,
|
||||||
}
|
}
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
opts.Page = page
|
opts.Page = page
|
||||||
rels, err := repo_model.GetReleasesByRepoID(gitRepo.Ctx, repo.ID, opts)
|
rels, err := db.Find[repo_model.Release](gitRepo.Ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,10 +159,13 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN")
|
||||||
|
|
||||||
// TODO: default value should be true in future releases
|
// TODO: default value should be true in future releases
|
||||||
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
||||||
|
|
||||||
if !DisableQueryAuthToken {
|
// warn if the setting is set to false explicitly
|
||||||
|
if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken {
|
||||||
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type BranchProtection struct {
|
|||||||
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||||
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||||
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||||
|
IgnoreStaleApprovals bool `json:"ignore_stale_approvals"`
|
||||||
RequireSignedCommits bool `json:"require_signed_commits"`
|
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||||
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||||
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
||||||
@@ -85,6 +86,7 @@ type CreateBranchProtectionOption struct {
|
|||||||
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
|
||||||
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
|
||||||
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
|
||||||
|
IgnoreStaleApprovals bool `json:"ignore_stale_approvals"`
|
||||||
RequireSignedCommits bool `json:"require_signed_commits"`
|
RequireSignedCommits bool `json:"require_signed_commits"`
|
||||||
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
ProtectedFilePatterns string `json:"protected_file_patterns"`
|
||||||
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
UnprotectedFilePatterns string `json:"unprotected_file_patterns"`
|
||||||
@@ -115,6 +117,7 @@ type EditBranchProtectionOption struct {
|
|||||||
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
|
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
|
||||||
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
|
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
|
||||||
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
|
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
|
||||||
|
IgnoreStaleApprovals *bool `json:"ignore_stale_approvals"`
|
||||||
RequireSignedCommits *bool `json:"require_signed_commits"`
|
RequireSignedCommits *bool `json:"require_signed_commits"`
|
||||||
ProtectedFilePatterns *string `json:"protected_file_patterns"`
|
ProtectedFilePatterns *string `json:"protected_file_patterns"`
|
||||||
UnprotectedFilePatterns *string `json:"unprotected_file_patterns"`
|
UnprotectedFilePatterns *string `json:"unprotected_file_patterns"`
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML {
|
|||||||
|
|
||||||
if labelScope == "" {
|
if labelScope == "" {
|
||||||
// Regular label
|
// Regular label
|
||||||
s := fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</div>",
|
s := fmt.Sprintf("<div class='ui label' style='color: %s !important; background-color: %s !important' data-tooltip-content title='%s'>%s</div>",
|
||||||
textColor, label.Color, description, RenderEmoji(ctx, label.Name))
|
textColor, label.Color, description, RenderEmoji(ctx, label.Name))
|
||||||
return template.HTML(s)
|
return template.HTML(s)
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML {
|
|||||||
itemColor := "#" + hex.EncodeToString(itemBytes)
|
itemColor := "#" + hex.EncodeToString(itemBytes)
|
||||||
scopeColor := "#" + hex.EncodeToString(scopeBytes)
|
scopeColor := "#" + hex.EncodeToString(scopeBytes)
|
||||||
|
|
||||||
s := fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+
|
s := fmt.Sprintf("<span class='ui label scope-parent' data-tooltip-content title='%s'>"+
|
||||||
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
||||||
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
||||||
"</span>",
|
"</span>",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@@ -25,6 +26,10 @@ func (su *StringUtils) Contains(s, substr string) bool {
|
|||||||
return strings.Contains(s, substr)
|
return strings.Contains(s, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (su *StringUtils) ReplaceAllStringRegex(s, regex, new string) string {
|
||||||
|
return regexp.MustCompile(regex).ReplaceAllString(s, new)
|
||||||
|
}
|
||||||
|
|
||||||
func (su *StringUtils) Split(s, sep string) []string {
|
func (su *StringUtils) Split(s, sep string) []string {
|
||||||
return strings.Split(s, sep)
|
return strings.Split(s, sep)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const (
|
|||||||
HookEventRepository HookEventType = "repository"
|
HookEventRepository HookEventType = "repository"
|
||||||
HookEventRelease HookEventType = "release"
|
HookEventRelease HookEventType = "release"
|
||||||
HookEventPackage HookEventType = "package"
|
HookEventPackage HookEventType = "package"
|
||||||
|
HookEventSchedule HookEventType = "schedule"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event returns the HookEventType as an event string
|
// Event returns the HookEventType as an event string
|
||||||
|
|||||||
@@ -423,6 +423,7 @@ authorization_failed_desc = The authorization failed because we detected an inva
|
|||||||
sspi_auth_failed = SSPI authentication failed
|
sspi_auth_failed = SSPI authentication failed
|
||||||
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
||||||
password_pwned_err = Could not complete request to HaveIBeenPwned
|
password_pwned_err = Could not complete request to HaveIBeenPwned
|
||||||
|
last_admin = You cannot remove the last admin. There must be at least one admin.
|
||||||
|
|
||||||
[mail]
|
[mail]
|
||||||
view_it_on = View it on %s
|
view_it_on = View it on %s
|
||||||
@@ -588,6 +589,8 @@ org_still_own_packages = "This organization still owns one or more packages, del
|
|||||||
|
|
||||||
target_branch_not_exist = Target branch does not exist.
|
target_branch_not_exist = Target branch does not exist.
|
||||||
|
|
||||||
|
admin_cannot_delete_self = You cannot delete yourself when you are an admin. Please remove your admin privileges first.
|
||||||
|
|
||||||
[user]
|
[user]
|
||||||
change_avatar = Change your avatar…
|
change_avatar = Change your avatar…
|
||||||
joined_on = Joined on %s
|
joined_on = Joined on %s
|
||||||
@@ -2322,6 +2325,8 @@ settings.protect_approvals_whitelist_users = Whitelisted reviewers:
|
|||||||
settings.protect_approvals_whitelist_teams = Whitelisted teams for reviews:
|
settings.protect_approvals_whitelist_teams = Whitelisted teams for reviews:
|
||||||
settings.dismiss_stale_approvals = Dismiss stale approvals
|
settings.dismiss_stale_approvals = Dismiss stale approvals
|
||||||
settings.dismiss_stale_approvals_desc = When new commits that change the content of the pull request are pushed to the branch, old approvals will be dismissed.
|
settings.dismiss_stale_approvals_desc = When new commits that change the content of the pull request are pushed to the branch, old approvals will be dismissed.
|
||||||
|
settings.ignore_stale_approvals = Ignore stale approvals
|
||||||
|
settings.ignore_stale_approvals_desc = Do not count approvals that were made on older commits (stale reviews) towards how many approvals the PR has. Irrelevant if stale reviews are already dismissed.
|
||||||
settings.require_signed_commits = Require Signed Commits
|
settings.require_signed_commits = Require Signed Commits
|
||||||
settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable.
|
settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable.
|
||||||
settings.protect_branch_name_pattern = Protected Branch Name Pattern
|
settings.protect_branch_name_pattern = Protected Branch Name Pattern
|
||||||
|
|||||||
+77
-13
@@ -512,19 +512,7 @@ func CommonRoutes() *web.Route {
|
|||||||
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
|
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
|
||||||
r.Get("/simple/{id}", pypi.PackageMetadata)
|
r.Get("/simple/{id}", pypi.PackageMetadata)
|
||||||
}, reqPackageAccess(perm.AccessModeRead))
|
}, reqPackageAccess(perm.AccessModeRead))
|
||||||
r.Group("/rpm", func() {
|
r.Group("/rpm", RpmRoutes(r), reqPackageAccess(perm.AccessModeRead))
|
||||||
r.Get(".repo", rpm.GetRepositoryConfig)
|
|
||||||
r.Get("/repository.key", rpm.GetRepositoryKey)
|
|
||||||
r.Put("/upload", reqPackageAccess(perm.AccessModeWrite), rpm.UploadPackageFile)
|
|
||||||
r.Group("/package/{name}/{version}/{architecture}", func() {
|
|
||||||
r.Get("", rpm.DownloadPackageFile)
|
|
||||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
|
|
||||||
})
|
|
||||||
r.Group("/repodata/{filename}", func() {
|
|
||||||
r.Head("", rpm.CheckRepositoryFileExistence)
|
|
||||||
r.Get("", rpm.GetRepositoryFile)
|
|
||||||
})
|
|
||||||
}, reqPackageAccess(perm.AccessModeRead))
|
|
||||||
r.Group("/rubygems", func() {
|
r.Group("/rubygems", func() {
|
||||||
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
||||||
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
|
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
|
||||||
@@ -589,6 +577,82 @@ func CommonRoutes() *web.Route {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support for uploading rpm packages with arbitrary depth paths
|
||||||
|
func RpmRoutes(r *web.Route) func() {
|
||||||
|
var (
|
||||||
|
groupRepoInfo = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)\.repo\z`)
|
||||||
|
groupUpload = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/upload\z`)
|
||||||
|
groupRpm = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`)
|
||||||
|
groupMetadata = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/repodata/([^/]+)\z`)
|
||||||
|
)
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "*", func(ctx *context.Context) {
|
||||||
|
path := ctx.Params("*")
|
||||||
|
isHead := ctx.Req.Method == "HEAD"
|
||||||
|
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
|
||||||
|
isPut := ctx.Req.Method == "PUT"
|
||||||
|
isDelete := ctx.Req.Method == "DELETE"
|
||||||
|
|
||||||
|
if path == "/repository.key" && isGetHead {
|
||||||
|
rpm.GetRepositoryKey(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get repo
|
||||||
|
m := groupRepoInfo.FindStringSubmatch(path)
|
||||||
|
if len(m) == 2 && isGetHead {
|
||||||
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
||||||
|
rpm.GetRepositoryConfig(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// get meta
|
||||||
|
m = groupMetadata.FindStringSubmatch(path)
|
||||||
|
if len(m) == 3 && isGetHead {
|
||||||
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
||||||
|
ctx.SetParams("filename", m[2])
|
||||||
|
if isHead {
|
||||||
|
rpm.CheckRepositoryFileExistence(ctx)
|
||||||
|
} else {
|
||||||
|
rpm.GetRepositoryFile(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// upload
|
||||||
|
m = groupUpload.FindStringSubmatch(path)
|
||||||
|
if len(m) == 2 && isPut {
|
||||||
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
||||||
|
rpm.UploadPackageFile(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// rpm down/delete
|
||||||
|
m = groupRpm.FindStringSubmatch(path)
|
||||||
|
if len(m) == 6 {
|
||||||
|
ctx.SetParams("group", strings.Trim(m[1], "/"))
|
||||||
|
ctx.SetParams("name", m[2])
|
||||||
|
ctx.SetParams("version", m[3])
|
||||||
|
ctx.SetParams("architecture", m[4])
|
||||||
|
if isGetHead {
|
||||||
|
rpm.DownloadPackageFile(ctx)
|
||||||
|
return
|
||||||
|
} else if isDelete {
|
||||||
|
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rpm.DeletePackageFile(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// default
|
||||||
|
ctx.Status(http.StatusNotFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerRoutes provides endpoints that implement the OCI API to serve containers
|
// ContainerRoutes provides endpoints that implement the OCI API to serve containers
|
||||||
// These have to be mounted on `/v2/...` to comply with the OCI spec:
|
// These have to be mounted on `/v2/...` to comply with the OCI spec:
|
||||||
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
|
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
|
||||||
|
|||||||
@@ -33,11 +33,14 @@ func apiError(ctx *context.Context, status int, obj any) {
|
|||||||
|
|
||||||
// https://dnf.readthedocs.io/en/latest/conf_ref.html
|
// https://dnf.readthedocs.io/en/latest/conf_ref.html
|
||||||
func GetRepositoryConfig(ctx *context.Context) {
|
func GetRepositoryConfig(ctx *context.Context) {
|
||||||
|
group := ctx.Params("group")
|
||||||
|
if group != "" {
|
||||||
|
group = fmt.Sprintf("/%s", group)
|
||||||
|
}
|
||||||
url := fmt.Sprintf("%sapi/packages/%s/rpm", setting.AppURL, ctx.Package.Owner.Name)
|
url := fmt.Sprintf("%sapi/packages/%s/rpm", setting.AppURL, ctx.Package.Owner.Name)
|
||||||
|
ctx.PlainText(http.StatusOK, `[gitea-`+ctx.Package.Owner.LowerName+strings.ReplaceAll(group, "/", "-")+`]
|
||||||
ctx.PlainText(http.StatusOK, `[gitea-`+ctx.Package.Owner.LowerName+`]
|
name=`+ctx.Package.Owner.Name+` - `+setting.AppName+strings.ReplaceAll(group, "/", " - ")+`
|
||||||
name=`+ctx.Package.Owner.Name+` - `+setting.AppName+`
|
baseurl=`+url+group+`/
|
||||||
baseurl=`+url+`
|
|
||||||
enabled=1
|
enabled=1
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
gpgkey=`+url+`/repository.key`)
|
gpgkey=`+url+`/repository.key`)
|
||||||
@@ -64,7 +67,7 @@ func CheckRepositoryFileExistence(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
|
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), ctx.Params("group"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, util.ErrNotExist) {
|
if errors.Is(err, util.ErrNotExist) {
|
||||||
ctx.Status(http.StatusNotFound)
|
ctx.Status(http.StatusNotFound)
|
||||||
@@ -94,6 +97,7 @@ func GetRepositoryFile(ctx *context.Context) {
|
|||||||
pv,
|
pv,
|
||||||
&packages_service.PackageFileInfo{
|
&packages_service.PackageFileInfo{
|
||||||
Filename: ctx.Params("filename"),
|
Filename: ctx.Params("filename"),
|
||||||
|
CompositeKey: ctx.Params("group"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -145,7 +149,7 @@ func UploadPackageFile(ctx *context.Context) {
|
|||||||
apiError(ctx, http.StatusInternalServerError, err)
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
group := ctx.Params("group")
|
||||||
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
||||||
ctx,
|
ctx,
|
||||||
&packages_service.PackageCreationInfo{
|
&packages_service.PackageCreationInfo{
|
||||||
@@ -153,7 +157,7 @@ func UploadPackageFile(ctx *context.Context) {
|
|||||||
Owner: ctx.Package.Owner,
|
Owner: ctx.Package.Owner,
|
||||||
PackageType: packages_model.TypeRpm,
|
PackageType: packages_model.TypeRpm,
|
||||||
Name: pck.Name,
|
Name: pck.Name,
|
||||||
Version: pck.Version,
|
Version: strings.Trim(fmt.Sprintf("%s/%s", group, pck.Version), "/"),
|
||||||
},
|
},
|
||||||
Creator: ctx.Doer,
|
Creator: ctx.Doer,
|
||||||
Metadata: pck.VersionMetadata,
|
Metadata: pck.VersionMetadata,
|
||||||
@@ -161,6 +165,7 @@ func UploadPackageFile(ctx *context.Context) {
|
|||||||
&packages_service.PackageFileCreationInfo{
|
&packages_service.PackageFileCreationInfo{
|
||||||
PackageFileInfo: packages_service.PackageFileInfo{
|
PackageFileInfo: packages_service.PackageFileInfo{
|
||||||
Filename: fmt.Sprintf("%s-%s.%s.rpm", pck.Name, pck.Version, pck.FileMetadata.Architecture),
|
Filename: fmt.Sprintf("%s-%s.%s.rpm", pck.Name, pck.Version, pck.FileMetadata.Architecture),
|
||||||
|
CompositeKey: group,
|
||||||
},
|
},
|
||||||
Creator: ctx.Doer,
|
Creator: ctx.Doer,
|
||||||
Data: buf,
|
Data: buf,
|
||||||
@@ -182,7 +187,7 @@ func UploadPackageFile(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rpm_service.BuildRepositoryFiles(ctx, ctx.Package.Owner.ID); err != nil {
|
if err := rpm_service.BuildRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil {
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -191,19 +196,20 @@ func UploadPackageFile(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DownloadPackageFile(ctx *context.Context) {
|
func DownloadPackageFile(ctx *context.Context) {
|
||||||
|
group := ctx.Params("group")
|
||||||
name := ctx.Params("name")
|
name := ctx.Params("name")
|
||||||
version := ctx.Params("version")
|
version := ctx.Params("version")
|
||||||
|
|
||||||
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
|
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
|
||||||
ctx,
|
ctx,
|
||||||
&packages_service.PackageInfo{
|
&packages_service.PackageInfo{
|
||||||
Owner: ctx.Package.Owner,
|
Owner: ctx.Package.Owner,
|
||||||
PackageType: packages_model.TypeRpm,
|
PackageType: packages_model.TypeRpm,
|
||||||
Name: name,
|
Name: name,
|
||||||
Version: version,
|
Version: strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"),
|
||||||
},
|
},
|
||||||
&packages_service.PackageFileInfo{
|
&packages_service.PackageFileInfo{
|
||||||
Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")),
|
Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")),
|
||||||
|
CompositeKey: group,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -219,14 +225,19 @@ func DownloadPackageFile(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeletePackageFile(webctx *context.Context) {
|
func DeletePackageFile(webctx *context.Context) {
|
||||||
|
group := webctx.Params("group")
|
||||||
name := webctx.Params("name")
|
name := webctx.Params("name")
|
||||||
version := webctx.Params("version")
|
version := webctx.Params("version")
|
||||||
architecture := webctx.Params("architecture")
|
architecture := webctx.Params("architecture")
|
||||||
|
|
||||||
var pd *packages_model.PackageDescriptor
|
var pd *packages_model.PackageDescriptor
|
||||||
|
|
||||||
err := db.WithTx(webctx, func(ctx stdctx.Context) error {
|
err := db.WithTx(webctx, func(ctx stdctx.Context) error {
|
||||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, webctx.Package.Owner.ID, packages_model.TypeRpm, name, version)
|
pv, err := packages_model.GetVersionByNameAndVersion(ctx,
|
||||||
|
webctx.Package.Owner.ID,
|
||||||
|
packages_model.TypeRpm,
|
||||||
|
name,
|
||||||
|
strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -235,7 +246,7 @@ func DeletePackageFile(webctx *context.Context) {
|
|||||||
ctx,
|
ctx,
|
||||||
pv.ID,
|
pv.ID,
|
||||||
fmt.Sprintf("%s-%s.%s.rpm", name, version, architecture),
|
fmt.Sprintf("%s-%s.%s.rpm", name, version, architecture),
|
||||||
packages_model.EmptyFileKey,
|
group,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -275,7 +286,7 @@ func DeletePackageFile(webctx *context.Context) {
|
|||||||
notify_service.PackageDelete(webctx, webctx.Doer, pd)
|
notify_service.PackageDelete(webctx, webctx.Doer, pd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rpm_service.BuildRepositoryFiles(webctx, webctx.Package.Owner.ID); err != nil {
|
if err := rpm_service.BuildRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil {
|
||||||
apiError(webctx, http.StatusInternalServerError, err)
|
apiError(webctx, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ func EditUser(ctx *context.APIContext) {
|
|||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/User"
|
// "$ref": "#/responses/User"
|
||||||
|
// "400":
|
||||||
|
// "$ref": "#/responses/error"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
@@ -264,6 +266,10 @@ func EditUser(ctx *context.APIContext) {
|
|||||||
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
|
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
|
||||||
}
|
}
|
||||||
if form.Admin != nil {
|
if form.Admin != nil {
|
||||||
|
if !*form.Admin && user_model.IsLastAdminUser(ctx, ctx.ContextUser) {
|
||||||
|
ctx.Error(http.StatusBadRequest, "LastAdmin", ctx.Tr("auth.last_admin"))
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.ContextUser.IsAdmin = *form.Admin
|
ctx.ContextUser.IsAdmin = *form.Admin
|
||||||
}
|
}
|
||||||
if form.AllowGitHook != nil {
|
if form.AllowGitHook != nil {
|
||||||
@@ -341,7 +347,8 @@ func DeleteUser(ctx *context.APIContext) {
|
|||||||
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) ||
|
if models.IsErrUserOwnRepos(err) ||
|
||||||
models.IsErrUserHasOrgs(err) ||
|
models.IsErrUserHasOrgs(err) ||
|
||||||
models.IsErrUserOwnPackages(err) {
|
models.IsErrUserOwnPackages(err) ||
|
||||||
|
models.IsErrDeleteLastAdminUser(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
||||||
|
|||||||
@@ -1156,9 +1156,9 @@ func Routes() *web.Route {
|
|||||||
m.Get("/subscribers", repo.ListSubscribers)
|
m.Get("/subscribers", repo.ListSubscribers)
|
||||||
m.Group("/subscription", func() {
|
m.Group("/subscription", func() {
|
||||||
m.Get("", user.IsWatching)
|
m.Get("", user.IsWatching)
|
||||||
m.Put("", reqToken(), user.Watch)
|
m.Put("", user.Watch)
|
||||||
m.Delete("", reqToken(), user.Unwatch)
|
m.Delete("", user.Unwatch)
|
||||||
})
|
}, reqToken())
|
||||||
m.Group("/releases", func() {
|
m.Group("/releases", func() {
|
||||||
m.Combo("").Get(repo.ListReleases).
|
m.Combo("").Get(repo.ListReleases).
|
||||||
Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
|
Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
|
||||||
|
|||||||
@@ -636,6 +636,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
|||||||
BlockOnRejectedReviews: form.BlockOnRejectedReviews,
|
BlockOnRejectedReviews: form.BlockOnRejectedReviews,
|
||||||
BlockOnOfficialReviewRequests: form.BlockOnOfficialReviewRequests,
|
BlockOnOfficialReviewRequests: form.BlockOnOfficialReviewRequests,
|
||||||
DismissStaleApprovals: form.DismissStaleApprovals,
|
DismissStaleApprovals: form.DismissStaleApprovals,
|
||||||
|
IgnoreStaleApprovals: form.IgnoreStaleApprovals,
|
||||||
RequireSignedCommits: form.RequireSignedCommits,
|
RequireSignedCommits: form.RequireSignedCommits,
|
||||||
ProtectedFilePatterns: form.ProtectedFilePatterns,
|
ProtectedFilePatterns: form.ProtectedFilePatterns,
|
||||||
UnprotectedFilePatterns: form.UnprotectedFilePatterns,
|
UnprotectedFilePatterns: form.UnprotectedFilePatterns,
|
||||||
@@ -830,6 +831,10 @@ func EditBranchProtection(ctx *context.APIContext) {
|
|||||||
protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
|
protectBranch.DismissStaleApprovals = *form.DismissStaleApprovals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.IgnoreStaleApprovals != nil {
|
||||||
|
protectBranch.IgnoreStaleApprovals = *form.IgnoreStaleApprovals
|
||||||
|
}
|
||||||
|
|
||||||
if form.RequireSignedCommits != nil {
|
if form.RequireSignedCommits != nil {
|
||||||
protectBranch.RequireSignedCommits = *form.RequireSignedCommits
|
protectBranch.RequireSignedCommits = *form.RequireSignedCommits
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@@ -53,7 +54,9 @@ func ListCollaborators(ctx *context.APIContext) {
|
|||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
count, err := repo_model.CountCollaborators(ctx, ctx.Repo.Repository.ID)
|
count, err := db.Count[repo_model.Collaboration](ctx, repo_model.FindCollaborationOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.InternalServerError(err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -102,14 +102,16 @@ func GetIssueDependencies(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastRepoID int64
|
repoPerms := make(map[int64]access_model.Permission)
|
||||||
var lastPerm access_model.Permission
|
repoPerms[ctx.Repo.Repository.ID] = ctx.Repo.Permission
|
||||||
for _, blocker := range blockersInfo {
|
for _, blocker := range blockersInfo {
|
||||||
// Get the permissions for this repository
|
// Get the permissions for this repository
|
||||||
perm := lastPerm
|
// If the repo ID exists in the map, return the exist permissions
|
||||||
if lastRepoID != blocker.Repository.ID {
|
// else get the permission and add it to the map
|
||||||
if blocker.Repository.ID == ctx.Repo.Repository.ID {
|
var perm access_model.Permission
|
||||||
perm = ctx.Repo.Permission
|
existPerm, ok := repoPerms[blocker.RepoID]
|
||||||
|
if ok {
|
||||||
|
perm = existPerm
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
|
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
|
||||||
@@ -117,8 +119,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
|
|||||||
ctx.ServerError("GetUserRepoPermission", err)
|
ctx.ServerError("GetUserRepoPermission", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
repoPerms[blocker.RepoID] = perm
|
||||||
lastRepoID = blocker.Repository.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check permission
|
// check permission
|
||||||
@@ -345,20 +346,23 @@ func GetIssueBlocks(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastRepoID int64
|
|
||||||
var lastPerm access_model.Permission
|
|
||||||
|
|
||||||
var issues []*issues_model.Issue
|
var issues []*issues_model.Issue
|
||||||
|
|
||||||
|
repoPerms := make(map[int64]access_model.Permission)
|
||||||
|
repoPerms[ctx.Repo.Repository.ID] = ctx.Repo.Permission
|
||||||
|
|
||||||
for i, depMeta := range deps {
|
for i, depMeta := range deps {
|
||||||
if i < skip || i >= max {
|
if i < skip || i >= max {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the permissions for this repository
|
// Get the permissions for this repository
|
||||||
perm := lastPerm
|
// If the repo ID exists in the map, return the exist permissions
|
||||||
if lastRepoID != depMeta.Repository.ID {
|
// else get the permission and add it to the map
|
||||||
if depMeta.Repository.ID == ctx.Repo.Repository.ID {
|
var perm access_model.Permission
|
||||||
perm = ctx.Repo.Permission
|
existPerm, ok := repoPerms[depMeta.RepoID]
|
||||||
|
if ok {
|
||||||
|
perm = existPerm
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
perm, err = access_model.GetUserRepoPermission(ctx, &depMeta.Repository, ctx.Doer)
|
perm, err = access_model.GetUserRepoPermission(ctx, &depMeta.Repository, ctx.Doer)
|
||||||
@@ -366,8 +370,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
|
|||||||
ctx.ServerError("GetUserRepoPermission", err)
|
ctx.ServerError("GetUserRepoPermission", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
repoPerms[depMeta.RepoID] = perm
|
||||||
lastRepoID = depMeta.Repository.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !perm.CanReadIssuesOrPulls(depMeta.Issue.IsPull) {
|
if !perm.CanReadIssuesOrPulls(depMeta.Issue.IsPull) {
|
||||||
|
|||||||
+10
-16
@@ -1329,17 +1329,16 @@ func GetPullRequestCommits(ctx *context.APIContext) {
|
|||||||
|
|
||||||
userCache := make(map[string]*user_model.User)
|
userCache := make(map[string]*user_model.User)
|
||||||
|
|
||||||
start, end := listOptions.GetStartEnd()
|
start, limit := listOptions.GetSkipTake()
|
||||||
|
|
||||||
if end > totalNumberOfCommits {
|
limit = min(limit, totalNumberOfCommits-start)
|
||||||
end = totalNumberOfCommits
|
limit = max(limit, 0)
|
||||||
}
|
|
||||||
|
|
||||||
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
|
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
|
||||||
files := ctx.FormString("files") == "" || ctx.FormBool("files")
|
files := ctx.FormString("files") == "" || ctx.FormBool("files")
|
||||||
|
|
||||||
apiCommits := make([]*api.Commit, 0, end-start)
|
apiCommits := make([]*api.Commit, 0, limit)
|
||||||
for i := start; i < end; i++ {
|
for i := start; i < start+limit; i++ {
|
||||||
apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, baseGitRepo, commits[i], userCache,
|
apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, baseGitRepo, commits[i], userCache,
|
||||||
convert.ToCommitOptions{
|
convert.ToCommitOptions{
|
||||||
Stat: true,
|
Stat: true,
|
||||||
@@ -1477,19 +1476,14 @@ func GetPullRequestFiles(ctx *context.APIContext) {
|
|||||||
totalNumberOfFiles := diff.NumFiles
|
totalNumberOfFiles := diff.NumFiles
|
||||||
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
|
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
|
||||||
|
|
||||||
start, end := listOptions.GetStartEnd()
|
start, limit := listOptions.GetSkipTake()
|
||||||
|
|
||||||
if end > totalNumberOfFiles {
|
limit = min(limit, totalNumberOfFiles-start)
|
||||||
end = totalNumberOfFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
lenFiles := end - start
|
limit = max(limit, 0)
|
||||||
if lenFiles < 0 {
|
|
||||||
lenFiles = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
apiFiles := make([]*api.ChangedFile, 0, lenFiles)
|
apiFiles := make([]*api.ChangedFile, 0, limit)
|
||||||
for i := start; i < end; i++ {
|
for i := start; i < start+limit; i++ {
|
||||||
apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
|
apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@@ -154,9 +155,10 @@ func ListReleases(ctx *context.APIContext) {
|
|||||||
IncludeTags: false,
|
IncludeTags: false,
|
||||||
IsDraft: ctx.FormOptionalBool("draft"),
|
IsDraft: ctx.FormOptionalBool("draft"),
|
||||||
IsPreRelease: ctx.FormOptionalBool("pre-release"),
|
IsPreRelease: ctx.FormOptionalBool("pre-release"),
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts)
|
releases, err := db.Find[repo_model.Release](ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
|
ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
|
||||||
return
|
return
|
||||||
@@ -170,7 +172,7 @@ func ListReleases(ctx *context.APIContext) {
|
|||||||
rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)
|
rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredCount, err := repo_model.CountReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts)
|
filteredCount, err := db.Count[repo_model.Release](ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.InternalServerError(err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -983,7 +983,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(units)+len(deleteUnitTypes) > 0 {
|
if len(units)+len(deleteUnitTypes) > 0 {
|
||||||
if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions) {
|
func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions) {
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, uid, listOptions)
|
keys, total, err := db.FindAndCount[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
ListOptions: listOptions,
|
||||||
|
OwnerID: uid,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
|
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := asymkey_model.GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
apiKeys := make([]*api.GPGKey, len(keys))
|
apiKeys := make([]*api.GPGKey, len(keys))
|
||||||
for i := range keys {
|
for i := range keys {
|
||||||
apiKeys[i] = convert.ToGPGKey(keys[i])
|
apiKeys[i] = convert.ToGPGKey(keys[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := asymkey_model.CountUserGPGKeys(ctx, uid)
|
|
||||||
if err != nil {
|
|
||||||
ctx.InternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.SetTotalCountHeader(total)
|
ctx.SetTotalCountHeader(total)
|
||||||
ctx.JSON(http.StatusOK, &apiKeys)
|
ctx.JSON(http.StatusOK, &apiKeys)
|
||||||
}
|
}
|
||||||
@@ -121,6 +123,10 @@ func GetGPGKey(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := key.LoadSubKeys(ctx); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "LoadSubKeys", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.JSON(http.StatusOK, convert.ToGPGKey(key))
|
ctx.JSON(http.StatusOK, convert.ToGPGKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +204,10 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
|
|||||||
ctx.Error(http.StatusInternalServerError, "VerifyUserGPGKey", err)
|
ctx.Error(http.StatusInternalServerError, "VerifyUserGPGKey", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := asymkey_model.GetGPGKeysByKeyID(ctx, form.KeyID)
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
KeyID: form.KeyID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if asymkey_model.IsErrGPGKeyNotExist(err) {
|
if asymkey_model.IsErrGPGKeyNotExist(err) {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
@@ -207,7 +216,7 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(http.StatusOK, convert.ToGPGKey(key[0]))
|
ctx.JSON(http.StatusOK, convert.ToGPGKey(keys[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:parameters userCurrentPostGPGKey
|
// swagger:parameters userCurrentPostGPGKey
|
||||||
|
|||||||
@@ -436,6 +436,12 @@ func EditUserPost(ctx *context.Context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether user is the last admin
|
||||||
|
if !form.Admin && user_model.IsLastAdminUser(ctx, u) {
|
||||||
|
ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
u.LoginName = form.LoginName
|
u.LoginName = form.LoginName
|
||||||
u.FullName = form.FullName
|
u.FullName = form.FullName
|
||||||
emailChanged := !strings.EqualFold(u.Email, form.Email)
|
emailChanged := !strings.EqualFold(u.Email, form.Email)
|
||||||
@@ -503,7 +509,10 @@ func DeleteUser(ctx *context.Context) {
|
|||||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
case models.IsErrUserOwnPackages(err):
|
case models.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
|
case models.IsErrDeleteLastAdminUser(err):
|
||||||
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("DeleteUser", err)
|
ctx.ServerError("DeleteUser", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package feed
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
|
||||||
@@ -14,8 +15,9 @@ import (
|
|||||||
|
|
||||||
// shows tags and/or releases on the repo as RSS / Atom feed
|
// shows tags and/or releases on the repo as RSS / Atom feed
|
||||||
func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleasesOnly bool, formatType string) {
|
func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleasesOnly bool, formatType string) {
|
||||||
releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
|
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
IncludeTags: !isReleasesOnly,
|
IncludeTags: !isReleasesOnly,
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReleasesByRepoID", err)
|
ctx.ServerError("GetReleasesByRepoID", err)
|
||||||
|
|||||||
@@ -845,6 +845,7 @@ func CompareDiff(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanWrite(unit.TypeProjects)
|
||||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||||
upload.AddUploadContext(ctx, "comment")
|
upload.AddUploadContext(ctx, "comment")
|
||||||
|
|
||||||
|
|||||||
+17
-21
@@ -1962,7 +1962,7 @@ func ViewIssue(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["BlockingDependencies"], ctx.Data["BlockingByDependenciesNotPermitted"] = checkBlockedByIssues(ctx, blocking)
|
ctx.Data["BlockingDependencies"], ctx.Data["BlockingDependenciesNotPermitted"] = checkBlockedByIssues(ctx, blocking)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -2023,16 +2023,16 @@ func ViewIssue(ctx *context.Context) {
|
|||||||
|
|
||||||
// checkBlockedByIssues return canRead and notPermitted
|
// checkBlockedByIssues return canRead and notPermitted
|
||||||
func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.DependencyInfo) (canRead, notPermitted []*issues_model.DependencyInfo) {
|
func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.DependencyInfo) (canRead, notPermitted []*issues_model.DependencyInfo) {
|
||||||
var (
|
repoPerms := make(map[int64]access_model.Permission)
|
||||||
lastRepoID int64
|
repoPerms[ctx.Repo.Repository.ID] = ctx.Repo.Permission
|
||||||
lastPerm access_model.Permission
|
for _, blocker := range blockers {
|
||||||
)
|
|
||||||
for i, blocker := range blockers {
|
|
||||||
// Get the permissions for this repository
|
// Get the permissions for this repository
|
||||||
perm := lastPerm
|
// If the repo ID exists in the map, return the exist permissions
|
||||||
if lastRepoID != blocker.Repository.ID {
|
// else get the permission and add it to the map
|
||||||
if blocker.Repository.ID == ctx.Repo.Repository.ID {
|
var perm access_model.Permission
|
||||||
perm = ctx.Repo.Permission
|
existPerm, ok := repoPerms[blocker.RepoID]
|
||||||
|
if ok {
|
||||||
|
perm = existPerm
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
|
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
|
||||||
@@ -2040,21 +2040,17 @@ func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.Depende
|
|||||||
ctx.ServerError("GetUserRepoPermission", err)
|
ctx.ServerError("GetUserRepoPermission", err)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
repoPerms[blocker.RepoID] = perm
|
||||||
}
|
}
|
||||||
lastRepoID = blocker.Repository.ID
|
if perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) {
|
||||||
}
|
canRead = append(canRead, blocker)
|
||||||
|
} else {
|
||||||
// check permission
|
notPermitted = append(notPermitted, blocker)
|
||||||
if !perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) {
|
|
||||||
blockers[len(notPermitted)], blockers[i] = blocker, blockers[len(notPermitted)]
|
|
||||||
notPermitted = blockers[:len(notPermitted)+1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blockers = blockers[len(notPermitted):]
|
sortDependencyInfo(canRead)
|
||||||
sortDependencyInfo(blockers)
|
|
||||||
sortDependencyInfo(notPermitted)
|
sortDependencyInfo(notPermitted)
|
||||||
|
return canRead, notPermitted
|
||||||
return blockers, notPermitted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortDependencyInfo(blockers []*issues_model.DependencyInfo) {
|
func sortDependencyInfo(blockers []*issues_model.DependencyInfo) {
|
||||||
|
|||||||
+34
-26
@@ -1149,30 +1149,28 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
switch {
|
switch {
|
||||||
case errors.Is(err, pull_service.ErrIsClosed):
|
case errors.Is(err, pull_service.ErrIsClosed):
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
|
ctx.JSONError(ctx.Tr("repo.pulls.is_closed"))
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
|
ctx.JSONError(ctx.Tr("repo.issues.closed_title"))
|
||||||
}
|
}
|
||||||
case errors.Is(err, pull_service.ErrUserNotAllowedToMerge):
|
case errors.Is(err, pull_service.ErrUserNotAllowedToMerge):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
|
ctx.JSONError(ctx.Tr("repo.pulls.update_not_allowed"))
|
||||||
case errors.Is(err, pull_service.ErrHasMerged):
|
case errors.Is(err, pull_service.ErrHasMerged):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
|
ctx.JSONError(ctx.Tr("repo.pulls.has_merged"))
|
||||||
case errors.Is(err, pull_service.ErrIsWorkInProgress):
|
case errors.Is(err, pull_service.ErrIsWorkInProgress):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip"))
|
||||||
case errors.Is(err, pull_service.ErrNotMergableState):
|
case errors.Is(err, pull_service.ErrNotMergableState):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||||
case models.IsErrDisallowedToMerge(err):
|
case models.IsErrDisallowedToMerge(err):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||||
case asymkey_service.IsErrWontSign(err):
|
case asymkey_service.IsErrWontSign(err):
|
||||||
ctx.Flash.Error(err.Error()) // has no translation ...
|
ctx.JSONError(err.Error()) // has no translation ...
|
||||||
case errors.Is(err, pull_service.ErrDependenciesLeft):
|
case errors.Is(err, pull_service.ErrDependenciesLeft):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
|
ctx.JSONError(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("WebCheck", err)
|
ctx.ServerError("WebCheck", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,18 +1178,18 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
if manuallyMerged {
|
if manuallyMerged {
|
||||||
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
case models.IsErrInvalidMergeStyle(err):
|
case models.IsErrInvalidMergeStyle(err):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||||
case strings.Contains(err.Error(), "Wrong commit ID"):
|
case strings.Contains(err.Error(), "Wrong commit ID"):
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.wrong_commit_id"))
|
ctx.JSONError(ctx.Tr("repo.pulls.wrong_commit_id"))
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("MergedManually", err)
|
ctx.ServerError("MergedManually", err)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSONRedirect(issue.Link())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1221,15 +1219,14 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
} else if scheduled {
|
} else if scheduled {
|
||||||
// nothing more to do ...
|
// nothing more to do ...
|
||||||
ctx.Flash.Success(ctx.Tr("repo.pulls.auto_merge_newly_scheduled"))
|
ctx.Flash.Success(ctx.Tr("repo.pulls.auto_merge_newly_scheduled"))
|
||||||
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pr.Index))
|
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, pr.Index))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
||||||
if models.IsErrInvalidMergeStyle(err) {
|
if models.IsErrInvalidMergeStyle(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||||
ctx.Redirect(issue.Link())
|
|
||||||
} else if models.IsErrMergeConflicts(err) {
|
} else if models.IsErrMergeConflicts(err) {
|
||||||
conflictError := err.(models.ErrMergeConflicts)
|
conflictError := err.(models.ErrMergeConflicts)
|
||||||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{
|
||||||
@@ -1242,7 +1239,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Flash.Error(flashError)
|
ctx.Flash.Error(flashError)
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
} else if models.IsErrRebaseConflicts(err) {
|
} else if models.IsErrRebaseConflicts(err) {
|
||||||
conflictError := err.(models.ErrRebaseConflicts)
|
conflictError := err.(models.ErrRebaseConflicts)
|
||||||
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{
|
||||||
@@ -1286,7 +1283,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Flash.Error(flashError)
|
ctx.Flash.Error(flashError)
|
||||||
}
|
}
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("Merge", err)
|
ctx.ServerError("Merge", err)
|
||||||
}
|
}
|
||||||
@@ -1295,7 +1292,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
log.Trace("Pull request merged: %d", pr.ID)
|
log.Trace("Pull request merged: %d", pr.ID)
|
||||||
|
|
||||||
if err := stopTimerIfAvailable(ctx, ctx.Doer, issue); err != nil {
|
if err := stopTimerIfAvailable(ctx, ctx.Doer, issue); err != nil {
|
||||||
ctx.ServerError("CreateOrStopIssueStopwatch", err)
|
ctx.ServerError("stopTimerIfAvailable", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1309,7 +1306,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if exist {
|
if exist {
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1327,7 +1324,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
deleteBranch(ctx, pr, headRepo)
|
deleteBranch(ctx, pr, headRepo)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CancelAutoMergePullRequest cancels a scheduled pr
|
// CancelAutoMergePullRequest cancels a scheduled pr
|
||||||
@@ -1387,7 +1384,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, *form, true)
|
labelIDs, assigneeIDs, milestoneID, projectID := ValidateRepoMetas(ctx, *form, true)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1465,6 +1462,17 @@ func CompareAndPullRequestPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if projectID > 0 {
|
||||||
|
if !ctx.Repo.CanWrite(unit.TypeProjects) {
|
||||||
|
ctx.Error(http.StatusBadRequest, "user hasn't the permission to write to projects")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := issues_model.ChangeProjectAssign(ctx, pullIssue, ctx.Doer, projectID); err != nil {
|
||||||
|
ctx.ServerError("ChangeProjectAssign", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
|
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
|
||||||
ctx.JSONRedirect(pullIssue.Link())
|
ctx.JSONRedirect(pullIssue.Link())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,9 +95,10 @@ func Releases(ctx *context.Context) {
|
|||||||
ListOptions: listOptions,
|
ListOptions: listOptions,
|
||||||
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
||||||
IncludeDrafts: writeAccess,
|
IncludeDrafts: writeAccess,
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts)
|
releases, err := db.Find[repo_model.Release](ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReleasesByRepoID", err)
|
ctx.ServerError("GetReleasesByRepoID", err)
|
||||||
return
|
return
|
||||||
@@ -194,9 +195,10 @@ func TagsList(ctx *context.Context) {
|
|||||||
IncludeDrafts: true,
|
IncludeDrafts: true,
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
HasSha1: util.OptionalBoolTrue,
|
HasSha1: util.OptionalBoolTrue,
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, opts)
|
releases, err := db.Find[repo_model.Release](ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReleasesByRepoID", err)
|
ctx.ServerError("GetReleasesByRepoID", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
@@ -74,8 +75,9 @@ func TestCalReleaseNumCommitsBehind(t *testing.T) {
|
|||||||
contexttest.LoadGitRepo(t, ctx)
|
contexttest.LoadGitRepo(t, ctx)
|
||||||
t.Cleanup(func() { ctx.Repo.GitRepo.Close() })
|
t.Cleanup(func() { ctx.Repo.GitRepo.Close() })
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
|
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
IncludeDrafts: ctx.Repo.CanWrite(unit.TypeReleases),
|
IncludeDrafts: ctx.Repo.CanWrite(unit.TypeReleases),
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -379,7 +379,10 @@ func RedirectDownload(ctx *context.Context) {
|
|||||||
)
|
)
|
||||||
tagNames := []string{vTag}
|
tagNames := []string{vTag}
|
||||||
curRepo := ctx.Repo.Repository
|
curRepo := ctx.Repo.Repository
|
||||||
releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, curRepo.ID, tagNames)
|
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
|
RepoID: curRepo.ID,
|
||||||
|
TagNames: tagNames,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("RedirectDownload", err)
|
ctx.ServerError("RedirectDownload", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ package setting
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"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/routers/web/repo"
|
"code.gitea.io/gitea/routers/web/repo"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetDefaultBranchPost set default branch
|
// SetDefaultBranchPost set default branch
|
||||||
@@ -35,24 +34,15 @@ func SetDefaultBranchPost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
branch := ctx.FormString("branch")
|
branch := ctx.FormString("branch")
|
||||||
if !ctx.Repo.GitRepo.IsBranchExist(branch) {
|
if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branch); err != nil {
|
||||||
|
switch {
|
||||||
|
case git_model.IsErrBranchNotExist(err):
|
||||||
ctx.Status(http.StatusNotFound)
|
ctx.Status(http.StatusNotFound)
|
||||||
return
|
default:
|
||||||
} else if repo.DefaultBranch != branch {
|
|
||||||
repo.DefaultBranch = branch
|
|
||||||
if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil {
|
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
ctx.ServerError("SetDefaultBranch", err)
|
ctx.ServerError("SetDefaultBranch", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if err := repo_model.UpdateDefaultBranch(ctx, repo); err != nil {
|
|
||||||
ctx.ServerError("SetDefaultBranch", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
notify_service.ChangeDefaultBranch(ctx, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
|||||||
protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews
|
protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews
|
||||||
protectBranch.BlockOnOfficialReviewRequests = f.BlockOnOfficialReviewRequests
|
protectBranch.BlockOnOfficialReviewRequests = f.BlockOnOfficialReviewRequests
|
||||||
protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
|
protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
|
||||||
|
protectBranch.IgnoreStaleApprovals = f.IgnoreStaleApprovals
|
||||||
protectBranch.RequireSignedCommits = f.RequireSignedCommits
|
protectBranch.RequireSignedCommits = f.RequireSignedCommits
|
||||||
protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
|
protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
|
||||||
protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns
|
protectBranch.UnprotectedFilePatterns = f.UnprotectedFilePatterns
|
||||||
|
|||||||
@@ -594,7 +594,7 @@ func SettingsPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
||||||
ctx.ServerError("UpdateRepositoryUnits", err)
|
ctx.ServerError("UpdateRepositoryUnits", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -663,7 +663,10 @@ func ShowSSHKeys(ctx *context.Context) {
|
|||||||
|
|
||||||
// ShowGPGKeys output all the public GPG keys of user by uid
|
// ShowGPGKeys output all the public GPG keys of user by uid
|
||||||
func ShowGPGKeys(ctx *context.Context) {
|
func ShowGPGKeys(ctx *context.Context) {
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, ctx.ContextUser.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
ListOptions: db.ListOptionsAll,
|
||||||
|
OwnerID: ctx.ContextUser.ID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("ListGPGKeys", err)
|
ctx.ServerError("ListGPGKeys", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -244,6 +244,13 @@ func DeleteAccount(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// admin should not delete themself
|
||||||
|
if ctx.Doer.IsAdmin {
|
||||||
|
ctx.Flash.Error(ctx.Tr("form.admin_cannot_delete_self"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrUserOwnRepos(err):
|
case models.IsErrUserOwnRepos(err):
|
||||||
@@ -255,6 +262,9 @@ func DeleteAccount(ctx *context.Context) {
|
|||||||
case models.IsErrUserOwnPackages(err):
|
case models.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
|
case models.IsErrDeleteLastAdminUser(err):
|
||||||
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("DeleteUser", err)
|
ctx.ServerError("DeleteUser", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,18 +277,29 @@ func loadKeysData(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Data["ExternalKeys"] = externalKeys
|
ctx.Data["ExternalKeys"] = externalKeys
|
||||||
|
|
||||||
gpgkeys, err := asymkey_model.ListGPGKeys(ctx, ctx.Doer.ID, db.ListOptions{})
|
gpgkeys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
ListOptions: db.ListOptionsAll,
|
||||||
|
OwnerID: ctx.Doer.ID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("ListGPGKeys", err)
|
ctx.ServerError("ListGPGKeys", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := asymkey_model.GPGKeyList(gpgkeys).LoadSubKeys(ctx); err != nil {
|
||||||
|
ctx.ServerError("LoadSubKeys", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.Data["GPGKeys"] = gpgkeys
|
ctx.Data["GPGKeys"] = gpgkeys
|
||||||
tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1)
|
tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1)
|
||||||
|
|
||||||
// generate a new aes cipher using the csrfToken
|
// generate a new aes cipher using the csrfToken
|
||||||
ctx.Data["TokenToSign"] = tokenToSign
|
ctx.Data["TokenToSign"] = tokenToSign
|
||||||
|
|
||||||
principals, err := asymkey_model.ListPrincipalKeys(ctx, ctx.Doer.ID, db.ListOptions{})
|
principals, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
|
||||||
|
ListOptions: db.ListOptionsAll,
|
||||||
|
OwnerID: ctx.Doer.ID,
|
||||||
|
KeyTypes: []asymkey_model.KeyType{asymkey_model.KeyTypePrincipal},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("ListPrincipalKeys", err)
|
ctx.ServerError("ListPrincipalKeys", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if unit_model.TypeActions.UnitGlobalDisabled() {
|
if unit_model.TypeActions.UnitGlobalDisabled() {
|
||||||
|
if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
|
||||||
|
log.Error("CleanRepoScheduleTasks: %v", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := input.Repo.LoadUnits(ctx); err != nil {
|
if err := input.Repo.LoadUnits(ctx); err != nil {
|
||||||
@@ -153,7 +156,11 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
|
|
||||||
var detectedWorkflows []*actions_module.DetectedWorkflow
|
var detectedWorkflows []*actions_module.DetectedWorkflow
|
||||||
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
|
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
|
||||||
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload)
|
workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit,
|
||||||
|
input.Event,
|
||||||
|
input.Payload,
|
||||||
|
input.Event == webhook_module.HookEventPush && input.Ref == input.Repo.DefaultBranch,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
@@ -167,7 +174,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget {
|
||||||
detectedWorkflows = append(detectedWorkflows, wf)
|
detectedWorkflows = append(detectedWorkflows, wf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +187,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||||
}
|
}
|
||||||
baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload)
|
baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
@@ -188,7 +195,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RepoPath(), baseCommit.ID)
|
log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RepoPath(), baseCommit.ID)
|
||||||
} else {
|
} else {
|
||||||
for _, wf := range baseWorkflows {
|
for _, wf := range baseWorkflows {
|
||||||
if wf.TriggerEvent == actions_module.GithubEventPullRequestTarget {
|
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
|
||||||
detectedWorkflows = append(detectedWorkflows, wf)
|
detectedWorkflows = append(detectedWorkflows, wf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,7 +272,7 @@ func handleWorkflows(
|
|||||||
IsForkPullRequest: isForkPullRequest,
|
IsForkPullRequest: isForkPullRequest,
|
||||||
Event: input.Event,
|
Event: input.Event,
|
||||||
EventPayload: string(p),
|
EventPayload: string(p),
|
||||||
TriggerEvent: dwf.TriggerEvent,
|
TriggerEvent: dwf.TriggerEvent.Name,
|
||||||
Status: actions_model.StatusWaiting,
|
Status: actions_model.StatusWaiting,
|
||||||
}
|
}
|
||||||
if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil {
|
if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil {
|
||||||
@@ -289,6 +296,7 @@ func handleWorkflows(
|
|||||||
run.RepoID,
|
run.RepoID,
|
||||||
run.Ref,
|
run.Ref,
|
||||||
run.WorkflowID,
|
run.WorkflowID,
|
||||||
|
run.Event,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Error("CancelRunningJobs: %v", err)
|
log.Error("CancelRunningJobs: %v", err)
|
||||||
}
|
}
|
||||||
@@ -414,8 +422,8 @@ func handleSchedules(
|
|||||||
log.Error("CountSchedules: %v", err)
|
log.Error("CountSchedules: %v", err)
|
||||||
return err
|
return err
|
||||||
} else if count > 0 {
|
} else if count > 0 {
|
||||||
if err := actions_model.DeleteScheduleTaskByRepo(ctx, input.Repo.ID); err != nil {
|
if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
|
||||||
log.Error("DeleteCronTaskByRepo: %v", err)
|
log.Error("CleanRepoScheduleTasks: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,19 +464,6 @@ func handleSchedules(
|
|||||||
Specs: schedules,
|
Specs: schedules,
|
||||||
Content: dwf.Content,
|
Content: dwf.Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel running jobs if the event is push
|
|
||||||
if run.Event == webhook_module.HookEventPush {
|
|
||||||
// cancel running jobs of the same workflow
|
|
||||||
if err := actions_model.CancelRunningJobs(
|
|
||||||
ctx,
|
|
||||||
run.RepoID,
|
|
||||||
run.Ref,
|
|
||||||
run.WorkflowID,
|
|
||||||
); err != nil {
|
|
||||||
log.Error("CancelRunningJobs: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crons = append(crons, run)
|
crons = append(crons, run)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ func startTasks(ctx context.Context) error {
|
|||||||
row.RepoID,
|
row.RepoID,
|
||||||
row.Schedule.Ref,
|
row.Schedule.Ref,
|
||||||
row.Schedule.WorkflowID,
|
row.Schedule.WorkflowID,
|
||||||
|
webhook_module.HookEventSchedule,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Error("CancelRunningJobs: %v", err)
|
log.Error("CancelRunningJobs: %v", err)
|
||||||
}
|
}
|
||||||
@@ -113,6 +114,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
|
|||||||
CommitSHA: cron.CommitSHA,
|
CommitSHA: cron.CommitSHA,
|
||||||
Event: cron.Event,
|
Event: cron.Event,
|
||||||
EventPayload: cron.EventPayload,
|
EventPayload: cron.EventPayload,
|
||||||
|
TriggerEvent: string(webhook_module.HookEventSchedule),
|
||||||
ScheduleID: cron.ID,
|
ScheduleID: cron.ID,
|
||||||
Status: actions_model.StatusWaiting,
|
Status: actions_model.StatusWaiting,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,10 @@ Loop:
|
|||||||
case always:
|
case always:
|
||||||
break Loop
|
break Loop
|
||||||
case pubkey:
|
case pubkey:
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, u.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
OwnerID: u.ID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, err
|
||||||
}
|
}
|
||||||
@@ -179,7 +182,10 @@ Loop:
|
|||||||
case always:
|
case always:
|
||||||
break Loop
|
break Loop
|
||||||
case pubkey:
|
case pubkey:
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, u.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
OwnerID: u.ID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, err
|
||||||
}
|
}
|
||||||
@@ -232,7 +238,10 @@ Loop:
|
|||||||
case always:
|
case always:
|
||||||
break Loop
|
break Loop
|
||||||
case pubkey:
|
case pubkey:
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, u.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
OwnerID: u.ID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, err
|
||||||
}
|
}
|
||||||
@@ -294,7 +303,10 @@ Loop:
|
|||||||
case always:
|
case always:
|
||||||
break Loop
|
break Loop
|
||||||
case pubkey:
|
case pubkey:
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, u.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
OwnerID: u.ID,
|
||||||
|
IncludeSubKeys: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-20
@@ -171,6 +171,7 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
|
|||||||
BlockOnOfficialReviewRequests: bp.BlockOnOfficialReviewRequests,
|
BlockOnOfficialReviewRequests: bp.BlockOnOfficialReviewRequests,
|
||||||
BlockOnOutdatedBranch: bp.BlockOnOutdatedBranch,
|
BlockOnOutdatedBranch: bp.BlockOnOutdatedBranch,
|
||||||
DismissStaleApprovals: bp.DismissStaleApprovals,
|
DismissStaleApprovals: bp.DismissStaleApprovals,
|
||||||
|
IgnoreStaleApprovals: bp.IgnoreStaleApprovals,
|
||||||
RequireSignedCommits: bp.RequireSignedCommits,
|
RequireSignedCommits: bp.RequireSignedCommits,
|
||||||
ProtectedFilePatterns: bp.ProtectedFilePatterns,
|
ProtectedFilePatterns: bp.ProtectedFilePatterns,
|
||||||
UnprotectedFilePatterns: bp.UnprotectedFilePatterns,
|
UnprotectedFilePatterns: bp.UnprotectedFilePatterns,
|
||||||
@@ -321,40 +322,38 @@ func ToTeam(ctx context.Context, team *organization.Team, loadOrg ...bool) (*api
|
|||||||
|
|
||||||
// ToTeams convert models.Team list to api.Team list
|
// ToTeams convert models.Team list to api.Team list
|
||||||
func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]*api.Team, error) {
|
func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]*api.Team, error) {
|
||||||
if len(teams) == 0 || teams[0] == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := make(map[int64]*api.Organization)
|
cache := make(map[int64]*api.Organization)
|
||||||
apiTeams := make([]*api.Team, len(teams))
|
apiTeams := make([]*api.Team, 0, len(teams))
|
||||||
for i := range teams {
|
for _, t := range teams {
|
||||||
if err := teams[i].LoadUnits(ctx); err != nil {
|
if err := t.LoadUnits(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apiTeams[i] = &api.Team{
|
apiTeam := &api.Team{
|
||||||
ID: teams[i].ID,
|
ID: t.ID,
|
||||||
Name: teams[i].Name,
|
Name: t.Name,
|
||||||
Description: teams[i].Description,
|
Description: t.Description,
|
||||||
IncludesAllRepositories: teams[i].IncludesAllRepositories,
|
IncludesAllRepositories: t.IncludesAllRepositories,
|
||||||
CanCreateOrgRepo: teams[i].CanCreateOrgRepo,
|
CanCreateOrgRepo: t.CanCreateOrgRepo,
|
||||||
Permission: teams[i].AccessMode.String(),
|
Permission: t.AccessMode.String(),
|
||||||
Units: teams[i].GetUnitNames(),
|
Units: t.GetUnitNames(),
|
||||||
UnitsMap: teams[i].GetUnitsMap(),
|
UnitsMap: t.GetUnitsMap(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if loadOrgs {
|
if loadOrgs {
|
||||||
apiOrg, ok := cache[teams[i].OrgID]
|
apiOrg, ok := cache[t.OrgID]
|
||||||
if !ok {
|
if !ok {
|
||||||
org, err := organization.GetOrgByID(ctx, teams[i].OrgID)
|
org, err := organization.GetOrgByID(ctx, t.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
apiOrg = ToOrganization(ctx, org)
|
apiOrg = ToOrganization(ctx, org)
|
||||||
cache[teams[i].OrgID] = apiOrg
|
cache[t.OrgID] = apiOrg
|
||||||
}
|
}
|
||||||
apiTeams[i].Organization = apiOrg
|
apiTeam.Organization = apiOrg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apiTeams = append(apiTeams, apiTeam)
|
||||||
}
|
}
|
||||||
return apiTeams, nil
|
return apiTeams, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,15 +21,9 @@ func ToPullReview(ctx context.Context, r *issues_model.Review, doer *user_model.
|
|||||||
r.Reviewer = user_model.NewGhostUser()
|
r.Reviewer = user_model.NewGhostUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
apiTeam, err := ToTeam(ctx, r.ReviewerTeam)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &api.PullReview{
|
result := &api.PullReview{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
Reviewer: ToUser(ctx, r.Reviewer, doer),
|
Reviewer: ToUser(ctx, r.Reviewer, doer),
|
||||||
ReviewerTeam: apiTeam,
|
|
||||||
State: api.ReviewStateUnknown,
|
State: api.ReviewStateUnknown,
|
||||||
Body: r.Content,
|
Body: r.Content,
|
||||||
CommitID: r.CommitID,
|
CommitID: r.CommitID,
|
||||||
@@ -43,6 +37,14 @@ func ToPullReview(ctx context.Context, r *issues_model.Review, doer *user_model.
|
|||||||
HTMLPullURL: r.Issue.HTMLURL(),
|
HTMLPullURL: r.Issue.HTMLURL(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.ReviewerTeam != nil {
|
||||||
|
var err error
|
||||||
|
result.ReviewerTeam, err = ToTeam(ctx, r.ReviewerTeam)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case issues_model.ReviewTypeApprove:
|
case issues_model.ReviewTypeApprove:
|
||||||
result.State = api.ReviewStateApproved
|
result.State = api.ReviewStateApproved
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@@ -133,7 +134,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
numReleases, _ := repo_model.GetReleaseCountByRepoID(ctx, repo.ID, repo_model.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false})
|
numReleases, _ := db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
|
IncludeDrafts: false,
|
||||||
|
IncludeTags: false,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
})
|
||||||
|
|
||||||
mirrorInterval := ""
|
mirrorInterval := ""
|
||||||
var mirrorUpdated time.Time
|
var mirrorUpdated time.Time
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ type ProtectBranchForm struct {
|
|||||||
BlockOnOfficialReviewRequests bool
|
BlockOnOfficialReviewRequests bool
|
||||||
BlockOnOutdatedBranch bool
|
BlockOnOutdatedBranch bool
|
||||||
DismissStaleApprovals bool
|
DismissStaleApprovals bool
|
||||||
|
IgnoreStaleApprovals bool
|
||||||
RequireSignedCommits bool
|
RequireSignedCommits bool
|
||||||
ProtectedFilePatterns string
|
ProtectedFilePatterns string
|
||||||
UnprotectedFilePatterns string
|
UnprotectedFilePatterns string
|
||||||
|
|||||||
@@ -83,22 +83,24 @@ func TestGiteaUploadRepo(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, labels, 12)
|
assert.Len(t, labels, 12)
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoID(db.DefaultContext, repo.ID, repo_model.FindReleasesOptions{
|
releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
Page: 0,
|
Page: 0,
|
||||||
},
|
},
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
|
RepoID: repo.ID,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, releases, 8)
|
assert.Len(t, releases, 8)
|
||||||
|
|
||||||
releases, err = repo_model.GetReleasesByRepoID(db.DefaultContext, repo.ID, repo_model.FindReleasesOptions{
|
releases, err = db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
Page: 0,
|
Page: 0,
|
||||||
},
|
},
|
||||||
IncludeTags: false,
|
IncludeTags: false,
|
||||||
|
RepoID: repo.ID,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, releases, 1)
|
assert.Len(t, releases, 1)
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ type packageData struct {
|
|||||||
|
|
||||||
type packageCache = map[*packages_model.PackageFile]*packageData
|
type packageCache = map[*packages_model.PackageFile]*packageData
|
||||||
|
|
||||||
// BuildRepositoryFiles builds metadata files for the repository
|
// BuildSpecificRepositoryFiles builds metadata files for the repository
|
||||||
func BuildRepositoryFiles(ctx context.Context, ownerID int64) error {
|
func BuildRepositoryFiles(ctx context.Context, ownerID int64, compositeKey string) error {
|
||||||
pv, err := GetOrCreateRepositoryVersion(ctx, ownerID)
|
pv, err := GetOrCreateRepositoryVersion(ctx, ownerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -136,6 +136,7 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64) error {
|
|||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
PackageType: packages_model.TypeRpm,
|
PackageType: packages_model.TypeRpm,
|
||||||
Query: "%.rpm",
|
Query: "%.rpm",
|
||||||
|
CompositeKey: compositeKey,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -194,15 +195,15 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64) error {
|
|||||||
cache[pf] = pd
|
cache[pf] = pd
|
||||||
}
|
}
|
||||||
|
|
||||||
primary, err := buildPrimary(ctx, pv, pfs, cache)
|
primary, err := buildPrimary(ctx, pv, pfs, cache, compositeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
filelists, err := buildFilelists(ctx, pv, pfs, cache)
|
filelists, err := buildFilelists(ctx, pv, pfs, cache, compositeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
other, err := buildOther(ctx, pv, pfs, cache)
|
other, err := buildOther(ctx, pv, pfs, cache, compositeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -216,11 +217,12 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64) error {
|
|||||||
filelists,
|
filelists,
|
||||||
other,
|
other,
|
||||||
},
|
},
|
||||||
|
compositeKey,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#repomd-xml
|
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#repomd-xml
|
||||||
func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID int64, data []*repoData) error {
|
func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID int64, data []*repoData, compositeKey string) error {
|
||||||
type Repomd struct {
|
type Repomd struct {
|
||||||
XMLName xml.Name `xml:"repomd"`
|
XMLName xml.Name `xml:"repomd"`
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
@@ -276,6 +278,7 @@ func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID
|
|||||||
&packages_service.PackageFileCreationInfo{
|
&packages_service.PackageFileCreationInfo{
|
||||||
PackageFileInfo: packages_service.PackageFileInfo{
|
PackageFileInfo: packages_service.PackageFileInfo{
|
||||||
Filename: file.Name,
|
Filename: file.Name,
|
||||||
|
CompositeKey: compositeKey,
|
||||||
},
|
},
|
||||||
Creator: user_model.NewGhostUser(),
|
Creator: user_model.NewGhostUser(),
|
||||||
Data: file.Data,
|
Data: file.Data,
|
||||||
@@ -292,7 +295,7 @@ func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#primary-xml
|
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#primary-xml
|
||||||
func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache) (*repoData, error) {
|
func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) {
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Epoch string `xml:"epoch,attr"`
|
Epoch string `xml:"epoch,attr"`
|
||||||
Version string `xml:"ver,attr"`
|
Version string `xml:"ver,attr"`
|
||||||
@@ -372,7 +375,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
|
|||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release)
|
||||||
packages = append(packages, &Package{
|
packages = append(packages, &Package{
|
||||||
Type: "rpm",
|
Type: "rpm",
|
||||||
Name: pd.Package.Name,
|
Name: pd.Package.Name,
|
||||||
@@ -401,7 +404,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
|
|||||||
Archive: pd.FileMetadata.ArchiveSize,
|
Archive: pd.FileMetadata.ArchiveSize,
|
||||||
},
|
},
|
||||||
Location: Location{
|
Location: Location{
|
||||||
Href: fmt.Sprintf("package/%s/%s/%s", url.PathEscape(pd.Package.Name), url.PathEscape(pd.Version.Version), url.PathEscape(pd.FileMetadata.Architecture)),
|
Href: fmt.Sprintf("package/%s/%s/%s/%s", url.PathEscape(pd.Package.Name), url.PathEscape(packageVersion), url.PathEscape(pd.FileMetadata.Architecture), url.PathEscape(fmt.Sprintf("%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture))),
|
||||||
},
|
},
|
||||||
Format: Format{
|
Format: Format{
|
||||||
License: pd.VersionMetadata.License,
|
License: pd.VersionMetadata.License,
|
||||||
@@ -431,11 +434,11 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
|
|||||||
XmlnsRpm: "http://linux.duke.edu/metadata/rpm",
|
XmlnsRpm: "http://linux.duke.edu/metadata/rpm",
|
||||||
PackageCount: len(pfs),
|
PackageCount: len(pfs),
|
||||||
Packages: packages,
|
Packages: packages,
|
||||||
})
|
}, compositeKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#filelists-xml
|
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#filelists-xml
|
||||||
func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache) (*repoData, error) { //nolint:dupl
|
func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) { //nolint:dupl
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Epoch string `xml:"epoch,attr"`
|
Epoch string `xml:"epoch,attr"`
|
||||||
Version string `xml:"ver,attr"`
|
Version string `xml:"ver,attr"`
|
||||||
@@ -478,11 +481,12 @@ func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs
|
|||||||
Xmlns: "http://linux.duke.edu/metadata/other",
|
Xmlns: "http://linux.duke.edu/metadata/other",
|
||||||
PackageCount: len(pfs),
|
PackageCount: len(pfs),
|
||||||
Packages: packages,
|
Packages: packages,
|
||||||
})
|
},
|
||||||
|
compositeKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#other-xml
|
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#other-xml
|
||||||
func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache) (*repoData, error) { //nolint:dupl
|
func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) { //nolint:dupl
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Epoch string `xml:"epoch,attr"`
|
Epoch string `xml:"epoch,attr"`
|
||||||
Version string `xml:"ver,attr"`
|
Version string `xml:"ver,attr"`
|
||||||
@@ -525,7 +529,7 @@ func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*p
|
|||||||
Xmlns: "http://linux.duke.edu/metadata/other",
|
Xmlns: "http://linux.duke.edu/metadata/other",
|
||||||
PackageCount: len(pfs),
|
PackageCount: len(pfs),
|
||||||
Packages: packages,
|
Packages: packages,
|
||||||
})
|
}, compositeKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writtenCounter counts all written bytes
|
// writtenCounter counts all written bytes
|
||||||
@@ -545,10 +549,8 @@ func (wc *writtenCounter) Written() int64 {
|
|||||||
return wc.written
|
return wc.written
|
||||||
}
|
}
|
||||||
|
|
||||||
func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filetype string, obj any) (*repoData, error) {
|
func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filetype string, obj any, compositeKey string) (*repoData, error) {
|
||||||
content, _ := packages_module.NewHashedBuffer()
|
content, _ := packages_module.NewHashedBuffer()
|
||||||
defer content.Close()
|
|
||||||
|
|
||||||
gzw := gzip.NewWriter(content)
|
gzw := gzip.NewWriter(content)
|
||||||
wc := &writtenCounter{}
|
wc := &writtenCounter{}
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
@@ -572,6 +574,7 @@ func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion,
|
|||||||
&packages_service.PackageFileCreationInfo{
|
&packages_service.PackageFileCreationInfo{
|
||||||
PackageFileInfo: packages_service.PackageFileInfo{
|
PackageFileInfo: packages_service.PackageFileInfo{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
|
CompositeKey: compositeKey,
|
||||||
},
|
},
|
||||||
Creator: user_model.NewGhostUser(),
|
Creator: user_model.NewGhostUser(),
|
||||||
Data: content,
|
Data: content,
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ func DeleteOldRepositoryArchives(ctx context.Context, olderThan time.Duration) e
|
|||||||
log.Trace("Doing: ArchiveCleanup")
|
log.Trace("Doing: ArchiveCleanup")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
archivers, err := repo_model.FindRepoArchives(ctx, repo_model.FindRepoArchiversOption{
|
archivers, err := db.Find[repo_model.RepoArchiver](ctx, repo_model.FindRepoArchiversOption{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: 100,
|
PageSize: 100,
|
||||||
Page: 1,
|
Page: 1,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
files_service "code.gitea.io/gitea/services/repository/files"
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
|
|
||||||
@@ -308,13 +310,28 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
|
|||||||
return "from_not_exist", nil
|
return "from_not_exist", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := git_model.RenameBranch(ctx, repo, from, to, func(isDefault bool) error {
|
if err := git_model.RenameBranch(ctx, repo, from, to, func(ctx context.Context, isDefault bool) error {
|
||||||
err2 := gitRepo.RenameBranch(from, to)
|
err2 := gitRepo.RenameBranch(from, to)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDefault {
|
if isDefault {
|
||||||
|
// if default branch changed, we need to delete all schedules and cron jobs
|
||||||
|
if err := actions_model.DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
|
||||||
|
log.Error("DeleteCronTaskByRepo: %v", err)
|
||||||
|
}
|
||||||
|
// cancel running cron jobs of this repository and delete old schedules
|
||||||
|
if err := actions_model.CancelRunningJobs(
|
||||||
|
ctx,
|
||||||
|
repo.ID,
|
||||||
|
from,
|
||||||
|
"",
|
||||||
|
webhook_module.HookEventSchedule,
|
||||||
|
); err != nil {
|
||||||
|
log.Error("CancelRunningJobs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
err2 = gitRepo.SetDefaultBranch(to)
|
err2 = gitRepo.SetDefaultBranch(to)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
@@ -450,3 +467,50 @@ func AddAllRepoBranchesToSyncQueue(ctx context.Context, doerID int64) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, newBranchName string) error {
|
||||||
|
if repo.DefaultBranch == newBranchName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gitRepo.IsBranchExist(newBranchName) {
|
||||||
|
return git_model.ErrBranchNotExist{
|
||||||
|
BranchName: newBranchName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldDefaultBranchName := repo.DefaultBranch
|
||||||
|
repo.DefaultBranch = newBranchName
|
||||||
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
if err := repo_model.UpdateDefaultBranch(ctx, repo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := actions_model.DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
|
||||||
|
log.Error("DeleteCronTaskByRepo: %v", err)
|
||||||
|
}
|
||||||
|
// cancel running cron jobs of this repository and delete old schedules
|
||||||
|
if err := actions_model.CancelRunningJobs(
|
||||||
|
ctx,
|
||||||
|
repo.ID,
|
||||||
|
oldDefaultBranchName,
|
||||||
|
"",
|
||||||
|
webhook_module.HookEventSchedule,
|
||||||
|
); err != nil {
|
||||||
|
log.Error("CancelRunningJobs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gitRepo.SetDefaultBranch(newBranchName); err != nil {
|
||||||
|
if !git.IsErrUnsupportedVersion(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_service.ChangeDefaultBranch(ctx, repo)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -327,9 +327,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
|
|||||||
lowerTags = append(lowerTags, strings.ToLower(tag))
|
lowerTags = append(lowerTags, strings.ToLower(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
|
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
TagNames: lowerTags,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetReleasesByRepoIDAndNames: %w", err)
|
return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
|
||||||
}
|
}
|
||||||
relMap := make(map[string]*repo_model.Release)
|
relMap := make(map[string]*repo_model.Release)
|
||||||
for _, rel := range releases {
|
for _, rel := range releases {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateRepositoryUnits updates a repository's units
|
||||||
|
func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) {
|
||||||
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
// Delete existing settings of units before adding again
|
||||||
|
for _, u := range units {
|
||||||
|
deleteUnitTypes = append(deleteUnitTypes, u.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
|
||||||
|
if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
|
||||||
|
log.Error("CleanRepoScheduleTasks: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(units) > 0 {
|
||||||
|
if err = db.Insert(ctx, units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
||||||
@@ -159,7 +159,9 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
|
|||||||
// ***** END: PublicKey *****
|
// ***** END: PublicKey *****
|
||||||
|
|
||||||
// ***** START: GPGPublicKey *****
|
// ***** START: GPGPublicKey *****
|
||||||
keys, err := asymkey_model.ListGPGKeys(ctx, u.ID, db.ListOptions{})
|
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
|
||||||
|
OwnerID: u.ID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ListGPGKeys: %w", err)
|
return fmt.Errorf("ListGPGKeys: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,10 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
|
|||||||
return fmt.Errorf("%s is an organization not a user", u.Name)
|
return fmt.Errorf("%s is an organization not a user", u.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user_model.IsLastAdminUser(ctx, u) {
|
||||||
|
return models.ErrDeleteLastAdminUser{UID: u.ID}
|
||||||
|
}
|
||||||
|
|
||||||
if purge {
|
if purge {
|
||||||
// Disable the user first
|
// Disable the user first
|
||||||
// NOTE: This is deliberately not within a transaction as it must disable the user immediately to prevent any further action by the user to be purged.
|
// NOTE: This is deliberately not within a transaction as it must disable the user immediately to prevent any further action by the user to be purged.
|
||||||
@@ -295,7 +299,8 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
|
|||||||
}
|
}
|
||||||
if err := DeleteUser(ctx, u, false); err != nil {
|
if err := DeleteUser(ctx, u, false); err != nil {
|
||||||
// Ignore users that were set inactive by admin.
|
// Ignore users that were set inactive by admin.
|
||||||
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) {
|
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
|
||||||
|
models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: use clustered lock (unique queue? or *abuse* cache)
|
// TODO: use clustered lock (unique queue? or *abuse* cache)
|
||||||
@@ -350,7 +351,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
|
|||||||
|
|
||||||
// DeleteWiki removes the actual and local copy of repository wiki.
|
// DeleteWiki removes the actual and local copy of repository wiki.
|
||||||
func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
|
func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
if err := repo_model.UpdateRepositoryUnits(ctx, repo, nil, []unit.Type{unit.TypeWiki}); err != nil {
|
if err := repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit.Type{unit.TypeWiki}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
{{if .DatabaseCheckInconsistentCollationColumns}}
|
{{if .DatabaseCheckInconsistentCollationColumns}}
|
||||||
<div class="ui red message">
|
<div class="ui red message">
|
||||||
{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}
|
{{ctx.Locale.Tr "admin.self_check.database_inconsistent_collation_columns" .DatabaseCheckResult.DatabaseCollation}}
|
||||||
<ul class="gt-w-100">
|
<ul class="gt-w-full">
|
||||||
{{range .DatabaseCheckInconsistentCollationColumns}}
|
{{range .DatabaseCheckInconsistentCollationColumns}}
|
||||||
<li>{{.}}</li>
|
<li>{{.}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div id="test-modal-form-1" class="ui mini modal">
|
<div id="test-modal-form-1" class="ui mini modal">
|
||||||
<div class="header">Form dialog (layout 1)</div>
|
<div class="header">Form dialog (layout 1)</div>
|
||||||
<form class="content" method="post">
|
<form class="content" method="post">
|
||||||
<div class="ui input gt-w-100"><input name="user_input"></div>
|
<div class="ui input gt-w-full"><input name="user_input"></div>
|
||||||
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="header">Form dialog (layout 2)</div>
|
<div class="header">Form dialog (layout 2)</div>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="ui input gt-w-100"><input name="user_input"></div>
|
<div class="ui input gt-w-full"><input name="user_input"></div>
|
||||||
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<div class="header">Form dialog (layout 3)</div>
|
<div class="header">Form dialog (layout 3)</div>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="ui input gt-w-100"><input name="user_input"></div>
|
<div class="ui input gt-w-full"><input name="user_input"></div>
|
||||||
</div>
|
</div>
|
||||||
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
||||||
</form>
|
</form>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<div id="test-modal-form-4" class="ui mini modal">
|
<div id="test-modal-form-4" class="ui mini modal">
|
||||||
<div class="header">Form dialog (layout 4)</div>
|
<div class="header">Form dialog (layout 4)</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="ui input gt-w-100"><input name="user_input"></div>
|
<div class="ui input gt-w-full"><input name="user_input"></div>
|
||||||
</div>
|
</div>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
{{else if $.IsOrganizationOwner}}
|
{{else if $.IsOrganizationOwner}}
|
||||||
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/action/join">
|
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/action/join">
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<button type="submit" class="ui primary small button" name="uid" value="{{$.SignedUser.ID}}">{{ctx.Locale.Tr "org.teams.join"}}</button>
|
<button type="submit" class="ui primary tiny button" name="uid" value="{{$.SignedUser.ID}}">{{ctx.Locale.Tr "org.teams.join"}}</button>
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,19 +4,23 @@
|
|||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.rpm.registry"}}</label>
|
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.rpm.registry"}}</label>
|
||||||
<div class="markup"><pre class="code-block"><code># {{ctx.Locale.Tr "packages.rpm.distro.redhat"}}
|
<div class="markup"><pre class="code-block"><code># {{ctx.Locale.Tr "packages.rpm.distros.redhat"}}
|
||||||
dnf config-manager --add-repo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm.repo"></gitea-origin-url>
|
{{$group_name:= StringUtils.ReplaceAllStringRegex .PackageDescriptor.Version.Version "(/[^/]+|[^/]*)\\z" "" -}}
|
||||||
|
{{- if $group_name -}}
|
||||||
|
{{- $group_name = (print "/" $group_name) -}}
|
||||||
|
{{- end -}}
|
||||||
|
dnf config-manager --add-repo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group_name}}.repo"></gitea-origin-url>
|
||||||
|
|
||||||
# {{ctx.Locale.Tr "packages.rpm.distro.suse"}}
|
# {{ctx.Locale.Tr "packages.rpm.distros.suse"}}
|
||||||
zypper addrepo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm.repo"></gitea-origin-url></code></pre></div>
|
zypper addrepo <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/rpm{{$group_name}}.repo"></gitea-origin-url></code></pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.rpm.install"}}</label>
|
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.rpm.install"}}</label>
|
||||||
<div class="markup">
|
<div class="markup">
|
||||||
<pre class="code-block"><code># {{ctx.Locale.Tr "packages.rpm.distro.redhat"}}
|
<pre class="code-block"><code># {{ctx.Locale.Tr "packages.rpm.distros.redhat"}}
|
||||||
dnf install {{$.PackageDescriptor.Package.Name}}
|
dnf install {{$.PackageDescriptor.Package.Name}}
|
||||||
|
|
||||||
# {{ctx.Locale.Tr "packages.rpm.distro.suse"}}
|
# {{ctx.Locale.Tr "packages.rpm.distros.suse"}}
|
||||||
zypper install {{$.PackageDescriptor.Package.Name}}</code></pre>
|
zypper install {{$.PackageDescriptor.Package.Name}}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -203,7 +203,7 @@
|
|||||||
{{if $showFileViewToggle}}
|
{{if $showFileViewToggle}}
|
||||||
{{/* for image or CSV, it can have a horizontal scroll bar, there won't be review comment context menu (position absolute) which would be clipped by "overflow" */}}
|
{{/* for image or CSV, it can have a horizontal scroll bar, there won't be review comment context menu (position absolute) which would be clipped by "overflow" */}}
|
||||||
<div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}} gt-overflow-x-scroll">
|
<div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}} gt-overflow-x-scroll">
|
||||||
<table class="chroma gt-w-100">
|
<table class="chroma gt-w-full">
|
||||||
{{if $isImage}}
|
{{if $isImage}}
|
||||||
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead "sniffedTypeBase" $sniffedTypeBase "sniffedTypeHead" $sniffedTypeHead}}
|
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead "sniffedTypeBase" $sniffedTypeBase "sniffedTypeHead" $sniffedTypeHead}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
+34
-30
@@ -2,36 +2,33 @@
|
|||||||
{{with .Repository}}
|
{{with .Repository}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="repo-header">
|
<div class="repo-header">
|
||||||
<div class="repo-title-wrap gt-df gt-fc">
|
<div class="flex-item gt-ac">
|
||||||
<div class="repo-title" role="heading" aria-level="1">
|
<div class="flex-item-leading">{{template "repo/icon" .}}</div>
|
||||||
<div class="gt-mr-3">
|
<div class="flex-item-main">
|
||||||
{{template "repo/icon" .}}
|
<div class="flex-item-title">
|
||||||
|
<a class="text light thin" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
|
||||||
|
<a class="text primary" href="{{$.RepoLink}}">{{.Name}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
|
<div class="flex-item-trailing">
|
||||||
<div class="gt-mx-2">/</div>
|
|
||||||
<a href="{{$.RepoLink}}">{{.Name}}</a>
|
|
||||||
<div class="labels gt-df gt-ac gt-fw">
|
|
||||||
{{if .IsArchived}}
|
{{if .IsArchived}}
|
||||||
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
|
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.archived"}}</span>
|
||||||
|
<div class="repo-icon" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.archived"}}">{{svg "octicon-archive" 18}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .IsPrivate}}
|
{{if .IsPrivate}}
|
||||||
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
|
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.private"}}</span>
|
||||||
|
<div class="repo-icon" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.private"}}">{{svg "octicon-lock" 18}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{if .Owner.Visibility.IsPrivate}}
|
{{if .Owner.Visibility.IsPrivate}}
|
||||||
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.internal"}}</span>
|
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.internal"}}</span>
|
||||||
|
<div class="repo-icon" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.internal"}}">{{svg "octicon-shield-lock" 18}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .IsTemplate}}
|
{{if .IsTemplate}}
|
||||||
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.template"}}</span>
|
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.template"}}</span>
|
||||||
|
<div class="repo-icon" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.template"}}">{{svg "octicon-repo-template" 18}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if $.PullMirror}}
|
|
||||||
<div class="fork-flag">{{ctx.Locale.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{$.PullMirror.RemoteAddress}}">{{$.PullMirror.RemoteAddress}}</a></div>
|
|
||||||
{{end}}
|
|
||||||
{{if .IsFork}}<div class="fork-flag">{{ctx.Locale.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}}
|
|
||||||
{{if .IsGenerated}}<div class="fork-flag">{{ctx.Locale.Tr "repo.generated_from"}} <a href="{{(.TemplateRepo ctx).Link}}">{{(.TemplateRepo ctx).FullName}}</a></div>{{end}}
|
|
||||||
</div>
|
|
||||||
{{if not (or .IsBeingCreated .IsBroken)}}
|
{{if not (or .IsBeingCreated .IsBroken)}}
|
||||||
<div class="repo-buttons">
|
<div class="repo-buttons">
|
||||||
{{if $.RepoTransfer}}
|
{{if $.RepoTransfer}}
|
||||||
@@ -62,7 +59,11 @@
|
|||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.watch_guest_user"}}"{{end}}>
|
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.watch_guest_user"}}"{{end}}>
|
||||||
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}}>
|
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}}>
|
||||||
{{if $.IsWatchingRepo}}{{svg "octicon-eye-closed" 16}}{{ctx.Locale.Tr "repo.unwatch"}}{{else}}{{svg "octicon-eye"}}{{ctx.Locale.Tr "repo.watch"}}{{end}}
|
{{if $.IsWatchingRepo}}
|
||||||
|
{{svg "octicon-eye-closed" 16}}<span class="text">{{ctx.Locale.Tr "repo.unwatch"}}</span>
|
||||||
|
{{else}}
|
||||||
|
{{svg "octicon-eye"}}<span class="text">{{ctx.Locale.Tr "repo.watch"}}</span>
|
||||||
|
{{end}}
|
||||||
</button>
|
</button>
|
||||||
<a class="ui basic label" href="{{.Link}}/watchers">
|
<a class="ui basic label" href="{{.Link}}/watchers">
|
||||||
{{CountFmt .NumWatches}}
|
{{CountFmt .NumWatches}}
|
||||||
@@ -74,7 +75,11 @@
|
|||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.star_guest_user"}}"{{end}}>
|
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.star_guest_user"}}"{{end}}>
|
||||||
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}}>
|
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}}>
|
||||||
{{if $.IsStaringRepo}}{{svg "octicon-star-fill"}}{{ctx.Locale.Tr "repo.unstar"}}{{else}}{{svg "octicon-star"}}{{ctx.Locale.Tr "repo.star"}}{{end}}
|
{{if $.IsStaringRepo}}
|
||||||
|
{{svg "octicon-star-fill"}}<span class="text">{{ctx.Locale.Tr "repo.unstar"}}</span>
|
||||||
|
{{else}}
|
||||||
|
{{svg "octicon-star"}}<span class="text">{{ctx.Locale.Tr "repo.star"}}</span>
|
||||||
|
{{end}}
|
||||||
</button>
|
</button>
|
||||||
<a class="ui basic label" href="{{.Link}}/stars">
|
<a class="ui basic label" href="{{.Link}}/stars">
|
||||||
{{CountFmt .NumStars}}
|
{{CountFmt .NumStars}}
|
||||||
@@ -107,7 +112,7 @@
|
|||||||
data-modal="#fork-repo-modal"
|
data-modal="#fork-repo-modal"
|
||||||
{{end}}
|
{{end}}
|
||||||
>
|
>
|
||||||
{{svg "octicon-repo-forked"}}{{ctx.Locale.Tr "repo.fork"}}
|
{{svg "octicon-repo-forked"}}<span class="text">{{ctx.Locale.Tr "repo.fork"}}</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="ui small modal" id="fork-repo-modal">
|
<div class="ui small modal" id="fork-repo-modal">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@@ -117,17 +122,13 @@
|
|||||||
<div class="ui list">
|
<div class="ui list">
|
||||||
{{range $.UserAndOrgForks}}
|
{{range $.UserAndOrgForks}}
|
||||||
<div class="ui item gt-py-3">
|
<div class="ui item gt-py-3">
|
||||||
<a href="{{.Link}}">
|
<a href="{{.Link}}">{{svg "octicon-repo-forked" 16 "gt-mr-3"}}{{.FullName}}</a>
|
||||||
{{svg "octicon-repo-forked" 16 "gt-mr-3"}}{{.FullName}}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if $.CanSignedUserFork}}
|
{{if $.CanSignedUserFork}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<a href="{{AppSubUrl}}/repo/fork/{{.ID}}">
|
<a href="{{AppSubUrl}}/repo/fork/{{.ID}}">{{ctx.Locale.Tr "repo.fork_to_different_account"}}</a>
|
||||||
{{ctx.Locale.Tr "repo.fork_to_different_account"}}
|
|
||||||
</a>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,12 +139,15 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div><!-- end grid -->
|
</div>
|
||||||
</div><!-- end container -->
|
{{if $.PullMirror}}<div class="fork-flag">{{ctx.Locale.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{$.PullMirror.RemoteAddress}}">{{$.PullMirror.RemoteAddress}}</a></div>{{end}}
|
||||||
|
{{if .IsFork}}<div class="fork-flag">{{ctx.Locale.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}}
|
||||||
|
{{if .IsGenerated}}<div class="fork-flag">{{ctx.Locale.Tr "repo.generated_from"}} <a href="{{(.TemplateRepo ctx).Link}}">{{(.TemplateRepo ctx).FullName}}</a></div>{{end}}
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="ui tabs container">
|
<div class="ui container secondary pointing tabular top attached borderless menu new-menu navbar">
|
||||||
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
|
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
|
||||||
<div class="ui tabular menu navbar gt-overflow-x-auto gt-overflow-y-hidden">
|
<div class="new-menu-inner">
|
||||||
{{if .Permission.CanRead $.UnitTypeCode}}
|
{{if .Permission.CanRead $.UnitTypeCode}}
|
||||||
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
|
<a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
|
||||||
{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
|
{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
|
||||||
@@ -228,14 +232,14 @@
|
|||||||
{{template "custom/extra_tabs" .}}
|
{{template "custom/extra_tabs" .}}
|
||||||
|
|
||||||
{{if .Permission.IsAdmin}}
|
{{if .Permission.IsAdmin}}
|
||||||
<a class="{{if .PageIsRepoSettings}}active {{end}}right item" href="{{.RepoLink}}/settings">
|
<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
|
||||||
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
|
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{else if .Permission.IsAdmin}}
|
{{else if .Permission.IsAdmin}}
|
||||||
<div class="ui tabular menu navbar gt-overflow-x-auto gt-overflow-y-hidden">
|
<div class="new-menu-inner">
|
||||||
<a class="{{if .PageIsRepoSettings}}active {{end}}right item" href="{{.RepoLink}}/settings">
|
<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
|
||||||
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
|
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="content gt-p-0 gt-w-100">
|
<div class="content gt-p-0 gt-w-full">
|
||||||
<div class="gt-df gt-items-start">
|
<div class="gt-df gt-items-start">
|
||||||
<div class="issue-card-icon">
|
<div class="issue-card-icon">
|
||||||
{{template "shared/issueicon" .}}
|
{{template "shared/issueicon" .}}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
{{range $key, $value := .Reactions}}
|
{{range $key, $value := .Reactions}}
|
||||||
{{$hasReacted := $value.HasUser $.ctxData.SignedUserID}}
|
{{$hasReacted := $value.HasUser $.ctxData.SignedUserID}}
|
||||||
<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not $.ctxData.IsSigned}} disabled{{end}} comment-reaction-button"
|
<a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not $.ctxData.IsSigned}} disabled{{end}} comment-reaction-button"
|
||||||
data-tooltip-content="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
|
data-tooltip-content
|
||||||
|
title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
|
||||||
|
aria-label="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
|
||||||
data-tooltip-placement="bottom-start"
|
data-tooltip-placement="bottom-start"
|
||||||
data-reaction-content="{{$key}}" data-has-reacted="{{$hasReacted}}">
|
data-reaction-content="{{$key}}" data-has-reacted="{{$hasReacted}}">
|
||||||
<span class="reaction">{{ReactionToEmoji $key}}</span>
|
<span class="reaction">{{ReactionToEmoji $key}}</span>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
<a href="{{.LFSFilesLink}}">{{ctx.Locale.Tr "repo.settings.lfs"}}</a> / <span class="truncate sha">{{.LFSFile.Oid}}</span>
|
<a href="{{.LFSFilesLink}}">{{ctx.Locale.Tr "repo.settings.lfs"}}</a> / <span class="truncate sha">{{.LFSFile.Oid}}</span>
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
{{if .EscapeStatus.Escaped}}
|
{{if .EscapeStatus.Escaped}}
|
||||||
<a class="ui mini basic button unescape-button gt-hidden">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</a>
|
<a class="ui tiny basic button unescape-button gt-hidden">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</a>
|
||||||
<a class="ui mini basic button escape-button">{{ctx.Locale.Tr "repo.escape_control_characters"}}</a>
|
<a class="ui tiny basic button escape-button">{{ctx.Locale.Tr "repo.escape_control_characters"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<a class="ui primary button" href="{{.LFSFilesLink}}/find?oid={{.LFSFile.Oid}}&size={{.LFSFile.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
|
<a class="ui primary tiny button" href="{{.LFSFilesLink}}/find?oid={{.LFSFile.Oid}}&size={{.LFSFile.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
<div class="ui attached table unstackable segment">
|
<div class="ui attached table unstackable segment">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<input type="hidden" name="oid" value="{{.Oid}} {{.Size}}">
|
<input type="hidden" name="oid" value="{{.Oid}} {{.Size}}">
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.lfs_pointers.associateAccessible" $.NumAssociatable}}</button>
|
<button class="ui primary tiny button">{{ctx.Locale.Tr "repo.settings.lfs_pointers.associateAccessible" $.NumAssociatable}}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -207,11 +207,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input name="dismiss_stale_approvals" type="checkbox" {{if .Rule.DismissStaleApprovals}}checked{{end}}>
|
<input id="dismiss_stale_approvals" name="dismiss_stale_approvals" type="checkbox" {{if .Rule.DismissStaleApprovals}}checked{{end}}>
|
||||||
<label>{{ctx.Locale.Tr "repo.settings.dismiss_stale_approvals"}}</label>
|
<label>{{ctx.Locale.Tr "repo.settings.dismiss_stale_approvals"}}</label>
|
||||||
<p class="help">{{ctx.Locale.Tr "repo.settings.dismiss_stale_approvals_desc"}}</p>
|
<p class="help">{{ctx.Locale.Tr "repo.settings.dismiss_stale_approvals_desc"}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="ignore_stale_approvals_box" class="field {{if .Rule.DismissStaleApprovals}}disabled{{end}}">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input id="ignore_stale_approvals" name="ignore_stale_approvals" type="checkbox" {{if .Rule.IgnoreStaleApprovals}}checked{{end}}>
|
||||||
|
<label>{{ctx.Locale.Tr "repo.settings.ignore_stale_approvals"}}</label>
|
||||||
|
<p class="help">{{ctx.Locale.Tr "repo.settings.ignore_stale_approvals_desc"}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="grouped fields">
|
<div class="grouped fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
|
|||||||
Generated
+15
@@ -677,6 +677,9 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/User"
|
"$ref": "#/responses/User"
|
||||||
},
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
"403": {
|
"403": {
|
||||||
"$ref": "#/responses/forbidden"
|
"$ref": "#/responses/forbidden"
|
||||||
},
|
},
|
||||||
@@ -17043,6 +17046,10 @@
|
|||||||
},
|
},
|
||||||
"x-go-name": "ForcePushWhitelistUsernames"
|
"x-go-name": "ForcePushWhitelistUsernames"
|
||||||
},
|
},
|
||||||
|
"ignore_stale_approvals": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "IgnoreStaleApprovals"
|
||||||
|
},
|
||||||
"merge_whitelist_teams": {
|
"merge_whitelist_teams": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -17710,6 +17717,10 @@
|
|||||||
},
|
},
|
||||||
"x-go-name": "ForcePushWhitelistUsernames"
|
"x-go-name": "ForcePushWhitelistUsernames"
|
||||||
},
|
},
|
||||||
|
"ignore_stale_approvals": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "IgnoreStaleApprovals"
|
||||||
|
},
|
||||||
"merge_whitelist_teams": {
|
"merge_whitelist_teams": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -18867,6 +18878,10 @@
|
|||||||
},
|
},
|
||||||
"x-go-name": "ForcePushWhitelistUsernames"
|
"x-go-name": "ForcePushWhitelistUsernames"
|
||||||
},
|
},
|
||||||
|
"ignore_stale_approvals": {
|
||||||
|
"type": "boolean",
|
||||||
|
"x-go-name": "IgnoreStaleApprovals"
|
||||||
|
},
|
||||||
"merge_whitelist_teams": {
|
"merge_whitelist_teams": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func TestPullRequestTargetEvent(t *testing.T) {
|
|||||||
assert.NotEmpty(t, baseRepo)
|
assert.NotEmpty(t, baseRepo)
|
||||||
|
|
||||||
// enable actions
|
// enable actions
|
||||||
err = repo_model.UpdateRepositoryUnits(db.DefaultContext, baseRepo, []repo_model.RepoUnit{{
|
err = repo_service.UpdateRepositoryUnits(db.DefaultContext, baseRepo, []repo_model.RepoUnit{{
|
||||||
RepoID: baseRepo.ID,
|
RepoID: baseRepo.ID,
|
||||||
Type: unit_model.TypeActions,
|
Type: unit_model.TypeActions,
|
||||||
}}, nil)
|
}}, nil)
|
||||||
@@ -216,7 +216,7 @@ func TestSkipCI(t *testing.T) {
|
|||||||
assert.NotEmpty(t, repo)
|
assert.NotEmpty(t, repo)
|
||||||
|
|
||||||
// enable actions
|
// enable actions
|
||||||
err = repo_model.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
|
err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Type: unit_model.TypeActions,
|
Type: unit_model.TypeActions,
|
||||||
}}, nil)
|
}}, nil)
|
||||||
|
|||||||
@@ -76,12 +76,12 @@ Mu0UFYgZ/bYnuvn/vz4wtCz8qMwsHUvP0PX3tbYFUctAPdrY6tiiDtcCddDECahx7SuVNP5dpmb5
|
|||||||
t.Run("RepositoryConfig", func(t *testing.T) {
|
t.Run("RepositoryConfig", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "GET", rootURL+".repo")
|
req := NewRequest(t, "GET", rootURL+"/el9/stable.repo")
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
expected := fmt.Sprintf(`[gitea-%s]
|
expected := fmt.Sprintf(`[gitea-%s-el9-stable]
|
||||||
name=%s - %s
|
name=%s - %s - el9 - stable
|
||||||
baseurl=%sapi/packages/%s/rpm
|
baseurl=%sapi/packages/%s/rpm/el9/stable/
|
||||||
enabled=1
|
enabled=1
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppName, setting.AppURL, user.Name, setting.AppURL, user.Name)
|
gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppName, setting.AppURL, user.Name, setting.AppURL, user.Name)
|
||||||
@@ -100,7 +100,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Upload", func(t *testing.T) {
|
t.Run("Upload", func(t *testing.T) {
|
||||||
url := rootURL + "/upload"
|
url := rootURL + "/el9/stable/upload"
|
||||||
|
|
||||||
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
|
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
|
||||||
MakeRequest(t, req, http.StatusUnauthorized)
|
MakeRequest(t, req, http.StatusUnauthorized)
|
||||||
@@ -118,7 +118,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
assert.Nil(t, pd.SemVer)
|
assert.Nil(t, pd.SemVer)
|
||||||
assert.IsType(t, &rpm_module.VersionMetadata{}, pd.Metadata)
|
assert.IsType(t, &rpm_module.VersionMetadata{}, pd.Metadata)
|
||||||
assert.Equal(t, packageName, pd.Package.Name)
|
assert.Equal(t, packageName, pd.Package.Name)
|
||||||
assert.Equal(t, packageVersion, pd.Version.Version)
|
assert.Equal(t, fmt.Sprintf("el9/stable/%s", packageVersion), pd.Version.Version)
|
||||||
|
|
||||||
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
|
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -138,7 +138,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
t.Run("Download", func(t *testing.T) {
|
t.Run("Download", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture))
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture))
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
assert.Equal(t, content, resp.Body.Bytes())
|
assert.Equal(t, content, resp.Body.Bytes())
|
||||||
@@ -147,7 +147,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
t.Run("Repository", func(t *testing.T) {
|
t.Run("Repository", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
url := rootURL + "/repodata"
|
url := rootURL + "/el9/stable/repodata"
|
||||||
|
|
||||||
req := NewRequest(t, "HEAD", url+"/dummy.xml")
|
req := NewRequest(t, "HEAD", url+"/dummy.xml")
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
@@ -201,8 +201,8 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
|
|
||||||
switch d.Type {
|
switch d.Type {
|
||||||
case "primary":
|
case "primary":
|
||||||
assert.EqualValues(t, 718, d.Size)
|
assert.EqualValues(t, 722, d.Size)
|
||||||
assert.EqualValues(t, 1729, d.OpenSize)
|
assert.EqualValues(t, 1759, d.OpenSize)
|
||||||
assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href)
|
assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href)
|
||||||
case "filelists":
|
case "filelists":
|
||||||
assert.EqualValues(t, 257, d.Size)
|
assert.EqualValues(t, 257, d.Size)
|
||||||
@@ -311,7 +311,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
assert.EqualValues(t, len(content), p.Size.Package)
|
assert.EqualValues(t, len(content), p.Size.Package)
|
||||||
assert.EqualValues(t, 13, p.Size.Installed)
|
assert.EqualValues(t, 13, p.Size.Installed)
|
||||||
assert.EqualValues(t, 272, p.Size.Archive)
|
assert.EqualValues(t, 272, p.Size.Archive)
|
||||||
assert.Equal(t, fmt.Sprintf("package/%s/%s/%s", packageName, packageVersion, packageArchitecture), p.Location.Href)
|
assert.Equal(t, fmt.Sprintf("package/%s/%s/%s/%s", packageName, packageVersion, packageArchitecture, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture)), p.Location.Href)
|
||||||
f := p.Format
|
f := p.Format
|
||||||
assert.Equal(t, "MIT", f.License)
|
assert.Equal(t, "MIT", f.License)
|
||||||
assert.Len(t, f.Provides.Entries, 2)
|
assert.Len(t, f.Provides.Entries, 2)
|
||||||
@@ -401,18 +401,17 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppN
|
|||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture))
|
req := NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture))
|
||||||
MakeRequest(t, req, http.StatusUnauthorized)
|
MakeRequest(t, req, http.StatusUnauthorized)
|
||||||
|
|
||||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)).
|
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
MakeRequest(t, req, http.StatusNoContent)
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
|
||||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm)
|
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, pvs)
|
assert.Empty(t, pvs)
|
||||||
|
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)).
|
||||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)).
|
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ func TestAPIWatch(t *testing.T) {
|
|||||||
t.Run("IsWatching", func(t *testing.T) {
|
t.Run("IsWatching", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription", repo)).
|
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription", repo))
|
||||||
|
MakeRequest(t, req, http.StatusUnauthorized)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription", repo)).
|
||||||
AddTokenAuth(tokenWithRepoScope)
|
AddTokenAuth(tokenWithRepoScope)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user