mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4126aad4aa | ||
|
|
5c96a2be87 | ||
|
|
acedf0f702 | ||
|
|
23139aa27b | ||
|
|
b6b71c78c4 | ||
|
|
2138661dae | ||
|
|
4b37eb2c23 | ||
|
|
dd2f007501 | ||
|
|
dd44c2164e | ||
|
|
2604571993 | ||
|
|
eae6985b63 | ||
|
|
d8583edfe7 | ||
|
|
d99479c810 | ||
|
|
fbe1f35112 | ||
|
|
25233a9bdc | ||
|
|
7a99c7b83c | ||
|
|
1d6e5c8e58 | ||
|
|
882e465c3a | ||
|
|
b139234fa8 | ||
|
|
d8b39324d7 | ||
|
|
9df573bddc | ||
|
|
b0a405c5fa | ||
|
|
3c53740244 | ||
|
|
da7d7e60d8 |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -4,6 +4,33 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||||
|
|
||||||
|
## [1.20.5](https://github.com/go-gitea/gitea/releases/tag/1.20.5) - 2023-10-03
|
||||||
|
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Fix z-index on markdown completion (#27237) (#27242 & #27238)
|
||||||
|
* Use secure cookie for HTTPS sites (#26999) (#27013)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix git 2.11 error when checking IsEmpty (#27393) (#27396)
|
||||||
|
* Allow get release download files and lfs files with oauth2 token format (#26430) (#27378)
|
||||||
|
* Fix orphan check for deleted branch (#27310) (#27320)
|
||||||
|
* Quote table `release` in sql queries (#27205) (#27219)
|
||||||
|
* Fix release URL in webhooks (#27182) (#27184)
|
||||||
|
* Fix successful return value for `SyncAndGetUserSpecificDiff` (#27152) (#27156)
|
||||||
|
* fix pagination for followers and following (#27127) (#27138)
|
||||||
|
* Fix issue templates when blank isses are disabled (#27061) (#27082)
|
||||||
|
* Fix context cache bug & enable context cache for dashabord commits' authors(#26991) (#27017)
|
||||||
|
* Fix INI parsing for value with trailing slash (#26995) (#27001)
|
||||||
|
* Fix PushEvent NullPointerException jenkinsci/github-plugin (#27203) (#27249)
|
||||||
|
* Fix organization field being null in POST /orgs/{orgid}/teams (#27150) (#27167 & #27162)
|
||||||
|
* Fix bug of review request number (#27406) (#27104)
|
||||||
|
* TESTING
|
||||||
|
* services/wiki: Close() after error handling (#27129) (#27137)
|
||||||
|
* DOCS
|
||||||
|
* Improve actions docs related to `pull_request` event (#27126) (#27145)
|
||||||
|
* MISC
|
||||||
|
* Add logs for data broken of comment review (#27326) (#27344)
|
||||||
|
* Load reviewer before sending notification (#27063) (#27064)
|
||||||
|
|
||||||
## [1.20.4](https://github.com/go-gitea/gitea/releases/tag/v1.20.4) - 2023-09-08
|
## [1.20.4](https://github.com/go-gitea/gitea/releases/tag/v1.20.4) - 2023-09-08
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
|||||||
@@ -1724,8 +1724,8 @@ LEVEL = Info
|
|||||||
;; Session cookie name
|
;; Session cookie name
|
||||||
;COOKIE_NAME = i_like_gitea
|
;COOKIE_NAME = i_like_gitea
|
||||||
;;
|
;;
|
||||||
;; If you use session in https only, default is false
|
;; If you use session in https only: true or false. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
|
||||||
;COOKIE_SECURE = false
|
;COOKIE_SECURE =
|
||||||
;;
|
;;
|
||||||
;; Session GC time interval in seconds, default is 86400 (1 day)
|
;; Session GC time interval in seconds, default is 86400 (1 day)
|
||||||
;GC_INTERVAL_TIME = 86400
|
;GC_INTERVAL_TIME = 86400
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||||||
- `ITERATE_BUFFER_SIZE`: **50**: Internal buffer size for iterating.
|
- `ITERATE_BUFFER_SIZE`: **50**: Internal buffer size for iterating.
|
||||||
- `CHARSET`: **utf8mb4**: For MySQL only, either "utf8" or "utf8mb4". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
|
- `CHARSET`: **utf8mb4**: For MySQL only, either "utf8" or "utf8mb4". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
|
||||||
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.
|
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.
|
||||||
- `LOG_SQL`: **true**: Log the executed SQL.
|
- `LOG_SQL`: **false**: Log the executed SQL.
|
||||||
- `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
|
- `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
|
||||||
- `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occurred.
|
- `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occurred.
|
||||||
- `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit.
|
- `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit.
|
||||||
@@ -772,7 +772,7 @@ and
|
|||||||
|
|
||||||
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, redis-cluster, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
|
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, redis-cluster, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
|
||||||
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
|
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
|
||||||
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
|
- `COOKIE_SECURE`:**_empty_**: `true` or `false`. Enable this to force using HTTPS for all session access. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
|
||||||
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
|
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
|
||||||
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
|
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
|
||||||
- `SESSION_LIFE_TIME`: **86400**: Session life time in seconds, default is 86400 (1 day)
|
- `SESSION_LIFE_TIME`: **86400**: Session life time in seconds, default is 86400 (1 day)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ menu:
|
|||||||
- `SSL_MODE`: MySQL 或 PostgreSQL数据库是否启用SSL模式。
|
- `SSL_MODE`: MySQL 或 PostgreSQL数据库是否启用SSL模式。
|
||||||
- `CHARSET`: **utf8mb4**: 仅当数据库为 MySQL 时有效, 可以为 "utf8" 或 "utf8mb4"。注意:如果使用 "utf8mb4",你的 MySQL InnoDB 版本必须在 5.6 以上。
|
- `CHARSET`: **utf8mb4**: 仅当数据库为 MySQL 时有效, 可以为 "utf8" 或 "utf8mb4"。注意:如果使用 "utf8mb4",你的 MySQL InnoDB 版本必须在 5.6 以上。
|
||||||
- `PATH`: SQLite3 数据文件存放路径。
|
- `PATH`: SQLite3 数据文件存放路径。
|
||||||
- `LOG_SQL`: **true**: 显示生成的SQL,默认为真。
|
- `LOG_SQL`: **false**: 显示生成的SQL,默认为真。
|
||||||
- `MAX_IDLE_CONNS` **0**: 最大空闲数据库连接
|
- `MAX_IDLE_CONNS` **0**: 最大空闲数据库连接
|
||||||
- `CONN_MAX_LIFETIME` **3s**: 数据库连接最大存活时间
|
- `CONN_MAX_LIFETIME` **3s**: 数据库连接最大存活时间
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ menu:
|
|||||||
|
|
||||||
- `PROVIDER`: Session 内容存储方式,可选 `memory`, `file`, `redis` 或 `mysql`。
|
- `PROVIDER`: Session 内容存储方式,可选 `memory`, `file`, `redis` 或 `mysql`。
|
||||||
- `PROVIDER_CONFIG`: 如果是文件,那么这里填根目录;其他的要填主机地址和端口。
|
- `PROVIDER_CONFIG`: 如果是文件,那么这里填根目录;其他的要填主机地址和端口。
|
||||||
- `COOKIE_SECURE`: 强制使用 HTTPS 作为session访问。
|
- `COOKIE_SECURE`: **_empty_**:`true` 或 `false`。启用此选项以强制在所有会话访问中使用 HTTPS。如果没有设置,当 ROOT_URL 是 https 链接的时候默认设置为 true。
|
||||||
- `GC_INTERVAL_TIME`: Session失效时间。
|
- `GC_INTERVAL_TIME`: Session失效时间。
|
||||||
|
|
||||||
## Picture (`picture`)
|
## Picture (`picture`)
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ menu:
|
|||||||
|
|
||||||
# Database Preparation
|
# Database Preparation
|
||||||
|
|
||||||
You need a database to use Gitea. Gitea supports PostgreSQL (>=10), MySQL (>=5.7), SQLite, and MSSQL (>=2008R2 SP3). This page will guide into preparing database. Only PostgreSQL and MySQL will be covered here since those database engines are widely-used in production. If you plan to use SQLite, you can ignore this chapter.
|
You need a database to use Gitea. Gitea supports PostgreSQL (>=10), MySQL (>=5.7), MariaDB, SQLite, and MSSQL (>=2008R2 SP3). This page will guide into preparing database. Only PostgreSQL and MySQL will be covered here since those database engines are widely-used in production. If you plan to use SQLite, you can ignore this chapter.
|
||||||
|
|
||||||
Database instance can be on same machine as Gitea (local database setup), or on different machine (remote database).
|
Database instance can be on same machine as Gitea (local database setup), or on different machine (remote database).
|
||||||
|
|
||||||
Note: All steps below requires that the database engine of your choice is installed on your system. For remote database setup, install the server application on database instance and client program on your Gitea server. The client program is used to test connection to the database from Gitea server, while Gitea itself use database driver provided by Go to accomplish the same thing. In addition, make sure you use same engine version for both server and client for some engine features to work. For security reason, protect `root` (MySQL) or `postgres` (PostgreSQL) database superuser with secure password. The steps assumes that you run Linux for both database and Gitea servers.
|
Note: All steps below requires that the database engine of your choice is installed on your system. For remote database setup, install the server application on database instance and client program on your Gitea server. The client program is used to test connection to the database from Gitea server, while Gitea itself use database driver provided by Go to accomplish the same thing. In addition, make sure you use same engine version for both server and client for some engine features to work. For security reason, protect `root` (MySQL) or `postgres` (PostgreSQL) database superuser with secure password. The steps assumes that you run Linux for both database and Gitea servers.
|
||||||
|
|
||||||
## MySQL
|
## MySQL/MariaDB
|
||||||
|
|
||||||
1. For remote database setup, you will need to make MySQL listen to your IP address. Edit `bind-address` option on `/etc/mysql/my.cnf` on database instance to:
|
1. For remote database setup, you will need to make MySQL listen to your IP address. Edit `bind-address` option on `/etc/mysql/my.cnf` on database instance to:
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ Note: All steps below requires that the database engine of your choice is instal
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
SET old_passwords=0;
|
SET old_passwords=0;
|
||||||
CREATE USER 'gitea' IDENTIFIED BY 'gitea';
|
CREATE USER 'gitea'@'%' IDENTIFIED BY 'gitea';
|
||||||
```
|
```
|
||||||
|
|
||||||
For remote database:
|
For remote database:
|
||||||
|
|||||||
@@ -180,3 +180,6 @@ For events supported only by GitHub, see GitHub's [documentation](https://docs.g
|
|||||||
| pull_request_review_comment | `created`, `edited` |
|
| pull_request_review_comment | `created`, `edited` |
|
||||||
| release | `published`, `edited` |
|
| release | `published`, `edited` |
|
||||||
| registry_package | `published` |
|
| registry_package | `published` |
|
||||||
|
|
||||||
|
> For `pull_request` events, in [GitHub Actions](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request), the `ref` is `refs/pull/:prNumber/merge`, which is a reference to the merge commit preview. However, Gitea has no such reference.
|
||||||
|
> Therefore, the `ref` in Gitea Actions is `refs/pull/:prNumber/head`, which points to the head of pull request rather than the preview of the merge commit.
|
||||||
|
|||||||
@@ -180,3 +180,6 @@ defaults:
|
|||||||
| pull_request_review_comment | `created`, `edited` |
|
| pull_request_review_comment | `created`, `edited` |
|
||||||
| release | `published`, `edited` |
|
| release | `published`, `edited` |
|
||||||
| registry_package | `published` |
|
| registry_package | `published` |
|
||||||
|
|
||||||
|
> 对于 `pull_request` 事件,在 [GitHub Actions](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) 中 `ref` 是 `refs/pull/:prNumber/merge`,它指向这个拉取请求合并提交的一个预览。但是 Gitea 没有这种 reference。
|
||||||
|
> 因此,Gitea Actions 中 `ref` 是 `refs/pull/:prNumber/head`,它指向这个拉取请求的头分支而不是合并提交的预览。
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
|
|||||||
|
|
||||||
// Published releases list
|
// Published releases list
|
||||||
sess := releasesForActivityStatement(repoID, fromTime)
|
sess := releasesForActivityStatement(repoID, fromTime)
|
||||||
sess.OrderBy("release.created_unix DESC")
|
sess.OrderBy("`release`.created_unix DESC")
|
||||||
stats.PublishedReleases = make([]*repo_model.Release, 0)
|
stats.PublishedReleases = make([]*repo_model.Release, 0)
|
||||||
if err = sess.Find(&stats.PublishedReleases); err != nil {
|
if err = sess.Find(&stats.PublishedReleases); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -350,7 +350,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
|
|||||||
|
|
||||||
// Published releases authors
|
// Published releases authors
|
||||||
sess = releasesForActivityStatement(repoID, fromTime)
|
sess = releasesForActivityStatement(repoID, fromTime)
|
||||||
if _, err = sess.Select("count(distinct release.publisher_id) as `count`").Table("release").Get(&count); err != nil {
|
if _, err = sess.Select("count(distinct `release`.publisher_id) as `count`").Table("release").Get(&count); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stats.PublishedReleaseAuthorCount = count
|
stats.PublishedReleaseAuthorCount = count
|
||||||
@@ -359,7 +359,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func releasesForActivityStatement(repoID int64, fromTime time.Time) *xorm.Session {
|
func releasesForActivityStatement(repoID int64, fromTime time.Time) *xorm.Session {
|
||||||
return db.GetEngine(db.DefaultContext).Where("release.repo_id = ?", repoID).
|
return db.GetEngine(db.DefaultContext).Where("`release`.repo_id = ?", repoID).
|
||||||
And("release.is_draft = ?", false).
|
And("`release`.is_draft = ?", false).
|
||||||
And("release.created_unix >= ?", fromTime.Unix())
|
And("`release`.created_unix >= ?", fromTime.Unix())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,12 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
|
|||||||
return DefaultAvatarLink()
|
return DefaultAvatarLink()
|
||||||
}
|
}
|
||||||
|
|
||||||
enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
|
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar,
|
||||||
|
setting.GetDefaultDisableGravatar(),
|
||||||
|
)
|
||||||
|
|
||||||
|
enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar,
|
||||||
|
setting.GetDefaultEnableFederatedAvatar(disableGravatar))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if enableFederatedAvatar && system_model.LibravatarService != nil {
|
if enableFederatedAvatar && system_model.LibravatarService != nil {
|
||||||
@@ -174,7 +179,6 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
|
|||||||
return urlStr
|
return urlStr
|
||||||
}
|
}
|
||||||
|
|
||||||
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
|
||||||
if !disableGravatar {
|
if !disableGravatar {
|
||||||
// copy GravatarSourceURL, because we will modify its Path.
|
// copy GravatarSourceURL, because we will modify its Path.
|
||||||
avatarURLCopy := *system_model.GravatarSourceURL
|
avatarURLCopy := *system_model.GravatarSourceURL
|
||||||
|
|||||||
@@ -140,3 +140,16 @@
|
|||||||
download_count: 0
|
download_count: 0
|
||||||
size: 0
|
size: 0
|
||||||
created_unix: 946684800
|
created_unix: 946684800
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 12
|
||||||
|
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22
|
||||||
|
repo_id: 2
|
||||||
|
issue_id: 0
|
||||||
|
release_id: 11
|
||||||
|
uploader_id: 2
|
||||||
|
comment_id: 0
|
||||||
|
name: README.md
|
||||||
|
download_count: 0
|
||||||
|
size: 0
|
||||||
|
created_unix: 946684800
|
||||||
|
|||||||
@@ -136,3 +136,17 @@
|
|||||||
is_prerelease: false
|
is_prerelease: false
|
||||||
is_tag: false
|
is_tag: false
|
||||||
created_unix: 946684803
|
created_unix: 946684803
|
||||||
|
|
||||||
|
- id: 11
|
||||||
|
repo_id: 2
|
||||||
|
publisher_id: 2
|
||||||
|
tag_name: "v1.1"
|
||||||
|
lower_tag_name: "v1.1"
|
||||||
|
target: ""
|
||||||
|
title: "v1.1"
|
||||||
|
sha1: "205ac761f3326a7ebe416e8673760016450b5cec"
|
||||||
|
num_commits: 2
|
||||||
|
is_draft: false
|
||||||
|
is_prerelease: false
|
||||||
|
is_tag: false
|
||||||
|
created_unix: 946684803
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-
|
-
|
||||||
id: 1
|
id: 1
|
||||||
setting_key: 'disable_gravatar'
|
setting_key: 'picture.disable_gravatar'
|
||||||
setting_value: 'false'
|
setting_value: 'false'
|
||||||
version: 1
|
version: 1
|
||||||
created: 1653533198
|
created: 1653533198
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
-
|
-
|
||||||
id: 2
|
id: 2
|
||||||
setting_key: 'enable_federated_avatar'
|
setting_key: 'picture.enable_federated_avatar'
|
||||||
setting_value: 'false'
|
setting_value: 'false'
|
||||||
version: 1
|
version: 1
|
||||||
created: 1653533198
|
created: 1653533198
|
||||||
|
|||||||
@@ -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"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommentList defines a list of comments
|
// CommentList defines a list of comments
|
||||||
@@ -422,37 +423,18 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
|
|||||||
|
|
||||||
reviewIDs := comments.getReviewIDs()
|
reviewIDs := comments.getReviewIDs()
|
||||||
reviews := make(map[int64]*Review, len(reviewIDs))
|
reviews := make(map[int64]*Review, len(reviewIDs))
|
||||||
left := len(reviewIDs)
|
if err := db.GetEngine(ctx).In("id", reviewIDs).Find(&reviews); err != nil {
|
||||||
for left > 0 {
|
return err
|
||||||
limit := db.DefaultMaxInSize
|
|
||||||
if left < limit {
|
|
||||||
limit = left
|
|
||||||
}
|
|
||||||
rows, err := db.GetEngine(ctx).
|
|
||||||
In("id", reviewIDs[:limit]).
|
|
||||||
Rows(new(Review))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var review Review
|
|
||||||
err = rows.Scan(&review)
|
|
||||||
if err != nil {
|
|
||||||
_ = rows.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reviews[review.ID] = &review
|
|
||||||
}
|
|
||||||
_ = rows.Close()
|
|
||||||
|
|
||||||
left -= limit
|
|
||||||
reviewIDs = reviewIDs[limit:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
comment.Review = reviews[comment.ReviewID]
|
comment.Review = reviews[comment.ReviewID]
|
||||||
|
if comment.Review == nil {
|
||||||
|
if comment.ReviewID > 0 {
|
||||||
|
log.Error("comment with review id [%d] but has no review record", comment.ReviewID)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// If the comment dismisses a review, we need to load the reviewer to show whose review has been dismissed.
|
// If the comment dismisses a review, we need to load the reviewer to show whose review has been dismissed.
|
||||||
// Otherwise, the reviewer is the poster of the comment, so we don't need to load it.
|
// Otherwise, the reviewer is the poster of the comment, so we don't need to load it.
|
||||||
|
|||||||
@@ -349,14 +349,21 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
|
|||||||
From("team_user").
|
From("team_user").
|
||||||
Where(builder.Eq{"team_user.uid": reviewRequestedID})
|
Where(builder.Eq{"team_user.uid": reviewRequestedID})
|
||||||
|
|
||||||
|
// if the review is approved or rejected, it should not be shown in the review requested list
|
||||||
|
maxReview := builder.Select("MAX(r.id)").
|
||||||
|
From("review as r").
|
||||||
|
Where(builder.In("r.type", []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest})).
|
||||||
|
GroupBy("r.issue_id, r.reviewer_id, r.reviewer_team_id")
|
||||||
|
|
||||||
subQuery := builder.Select("review.issue_id").
|
subQuery := builder.Select("review.issue_id").
|
||||||
From("review").
|
From("review").
|
||||||
Where(builder.And(
|
Where(builder.And(
|
||||||
builder.In("review.type", []ReviewType{ReviewTypeRequest, ReviewTypeReject, ReviewTypeApprove}),
|
builder.Eq{"review.type": ReviewTypeRequest},
|
||||||
builder.Or(
|
builder.Or(
|
||||||
builder.Eq{"review.reviewer_id": reviewRequestedID},
|
builder.Eq{"review.reviewer_id": reviewRequestedID},
|
||||||
builder.In("review.reviewer_team_id", existInTeamQuery),
|
builder.In("review.reviewer_team_id", existInTeamQuery),
|
||||||
),
|
),
|
||||||
|
builder.In("review.id", maxReview),
|
||||||
))
|
))
|
||||||
return sess.Where("issue.poster_id <> ?", reviewRequestedID).
|
return sess.Where("issue.poster_id <> ?", reviewRequestedID).
|
||||||
And(builder.In("issue.id", subQuery))
|
And(builder.In("issue.id", subQuery))
|
||||||
|
|||||||
@@ -94,11 +94,14 @@ func GetSetting(ctx context.Context, key string) (*Setting, error) {
|
|||||||
const contextCacheKey = "system_setting"
|
const contextCacheKey = "system_setting"
|
||||||
|
|
||||||
// GetSettingWithCache returns the setting value via the key
|
// GetSettingWithCache returns the setting value via the key
|
||||||
func GetSettingWithCache(ctx context.Context, key string) (string, error) {
|
func GetSettingWithCache(ctx context.Context, key, defaultVal string) (string, error) {
|
||||||
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
|
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
|
||||||
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
|
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
|
||||||
res, err := GetSetting(ctx, key)
|
res, err := GetSetting(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if IsErrSettingIsNotExist(err) {
|
||||||
|
return defaultVal, nil
|
||||||
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return res.SettingValue, nil
|
return res.SettingValue, nil
|
||||||
@@ -108,17 +111,21 @@ func GetSettingWithCache(ctx context.Context, key string) (string, error) {
|
|||||||
|
|
||||||
// GetSettingBool return bool value of setting,
|
// GetSettingBool return bool value of setting,
|
||||||
// none existing keys and errors are ignored and result in false
|
// none existing keys and errors are ignored and result in false
|
||||||
func GetSettingBool(ctx context.Context, key string) bool {
|
func GetSettingBool(ctx context.Context, key string, defaultVal bool) (bool, error) {
|
||||||
s, _ := GetSetting(ctx, key)
|
s, err := GetSetting(ctx, key)
|
||||||
if s == nil {
|
switch {
|
||||||
return false
|
case err == nil:
|
||||||
|
v, _ := strconv.ParseBool(s.SettingValue)
|
||||||
|
return v, nil
|
||||||
|
case IsErrSettingIsNotExist(err):
|
||||||
|
return defaultVal, nil
|
||||||
|
default:
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
v, _ := strconv.ParseBool(s.SettingValue)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSettingWithCacheBool(ctx context.Context, key string) bool {
|
func GetSettingWithCacheBool(ctx context.Context, key string, defaultVal bool) bool {
|
||||||
s, _ := GetSettingWithCache(ctx, key)
|
s, _ := GetSettingWithCache(ctx, key, strconv.FormatBool(defaultVal))
|
||||||
v, _ := strconv.ParseBool(s)
|
v, _ := strconv.ParseBool(s)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -259,52 +266,41 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Init(ctx context.Context) error {
|
func Init(ctx context.Context) error {
|
||||||
var disableGravatar bool
|
disableGravatar, err := GetSettingBool(ctx, KeyPictureDisableGravatar, setting_module.GetDefaultDisableGravatar())
|
||||||
disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar)
|
if err != nil {
|
||||||
if IsErrSettingIsNotExist(err) {
|
|
||||||
disableGravatar = setting_module.GetDefaultDisableGravatar()
|
|
||||||
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
disableGravatar = disableGravatarSetting.GetValueBool()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var enableFederatedAvatar bool
|
enableFederatedAvatar, err := GetSettingBool(ctx, KeyPictureEnableFederatedAvatar, setting_module.GetDefaultEnableFederatedAvatar(disableGravatar))
|
||||||
enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar)
|
if err != nil {
|
||||||
if IsErrSettingIsNotExist(err) {
|
|
||||||
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
|
|
||||||
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
enableFederatedAvatar = disableGravatarSetting.GetValueBool()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting_module.OfflineMode {
|
if setting_module.OfflineMode {
|
||||||
disableGravatar = true
|
if !disableGravatar {
|
||||||
enableFederatedAvatar = false
|
|
||||||
if !GetSettingBool(ctx, KeyPictureDisableGravatar) {
|
|
||||||
if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
|
if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
|
||||||
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
|
return fmt.Errorf("failed to set setting %q: %w", KeyPictureDisableGravatar, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) {
|
disableGravatar = true
|
||||||
|
|
||||||
|
if enableFederatedAvatar {
|
||||||
if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
|
if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
|
||||||
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
|
return fmt.Errorf("failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enableFederatedAvatar = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableFederatedAvatar || !disableGravatar {
|
if enableFederatedAvatar || !disableGravatar {
|
||||||
var err error
|
var err error
|
||||||
GravatarSourceURL, err = url.Parse(setting_module.GravatarSource)
|
GravatarSourceURL, err = url.Parse(setting_module.GravatarSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse Gravatar URL(%s): %w", setting_module.GravatarSource, err)
|
return fmt.Errorf("failed to parse Gravatar URL(%s): %w", setting_module.GravatarSource, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if GravatarSourceURL != nil && enableFederatedAvatarSetting.GetValueBool() {
|
if GravatarSourceURL != nil && enableFederatedAvatar {
|
||||||
LibravatarService = libravatar.New()
|
LibravatarService = libravatar.New()
|
||||||
if GravatarSourceURL.Scheme == "https" {
|
if GravatarSourceURL.Scheme == "https" {
|
||||||
LibravatarService.SetUseHTTPS(true)
|
LibravatarService.SetUseHTTPS(true)
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
|
|||||||
useLocalAvatar := false
|
useLocalAvatar := false
|
||||||
autoGenerateAvatar := false
|
autoGenerateAvatar := false
|
||||||
|
|
||||||
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar,
|
||||||
|
setting.GetDefaultDisableGravatar(),
|
||||||
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case u.UseCustomAvatar:
|
case u.UseCustomAvatar:
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
|||||||
},
|
},
|
||||||
// find releases without existing repository
|
// find releases without existing repository
|
||||||
genericOrphanCheck("Orphaned Releases without existing repository",
|
genericOrphanCheck("Orphaned Releases without existing repository",
|
||||||
"release", "repository", "release.repo_id=repository.id"),
|
"release", "repository", "`release`.repo_id=repository.id"),
|
||||||
// find pulls without existing issues
|
// find pulls without existing issues
|
||||||
genericOrphanCheck("Orphaned PullRequests without existing issue",
|
genericOrphanCheck("Orphaned PullRequests without existing issue",
|
||||||
"pull_request", "issue", "pull_request.issue_id=issue.id"),
|
"pull_request", "issue", "pull_request.issue_id=issue.id"),
|
||||||
@@ -168,9 +168,9 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
|||||||
// find protected branches without existing repository
|
// find protected branches without existing repository
|
||||||
genericOrphanCheck("Protected Branches without existing repository",
|
genericOrphanCheck("Protected Branches without existing repository",
|
||||||
"protected_branch", "repository", "protected_branch.repo_id=repository.id"),
|
"protected_branch", "repository", "protected_branch.repo_id=repository.id"),
|
||||||
// find deleted branches without existing repository
|
// find branches without existing repository
|
||||||
genericOrphanCheck("Deleted Branches without existing repository",
|
genericOrphanCheck("Branches without existing repository",
|
||||||
"deleted_branch", "repository", "deleted_branch.repo_id=repository.id"),
|
"branch", "repository", "branch.repo_id=repository.id"),
|
||||||
// find LFS locks without existing repository
|
// find LFS locks without existing repository
|
||||||
genericOrphanCheck("LFS locks without existing repository",
|
genericOrphanCheck("LFS locks without existing repository",
|
||||||
"lfs_lock", "repository", "lfs_lock.repo_id=repository.id"),
|
"lfs_lock", "repository", "lfs_lock.repo_id=repository.id"),
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ func (repo *Repository) IsEmpty() (bool, error) {
|
|||||||
Stdout: &output,
|
Stdout: &output,
|
||||||
Stderr: &errbuf,
|
Stderr: &errbuf,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if err.Error() == "exit status 1" && errbuf.String() == "" {
|
if (err.Error() == "exit status 1" && strings.TrimSpace(errbuf.String()) == "") || err.Error() == "exit status 129" {
|
||||||
|
// git 2.11 exits with 129 if the repo is empty
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return true, fmt.Errorf("check empty: %w - %s", err, errbuf.String())
|
return true, fmt.Errorf("check empty: %w - %s", err, errbuf.String())
|
||||||
|
|||||||
@@ -177,6 +177,9 @@ func (m *mailNotifier) NotifyPullRequestPushCommits(ctx context.Context, doer *u
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mailNotifier) NotifyPullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
|
func (m *mailNotifier) NotifyPullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
|
||||||
|
if err := comment.Review.LoadReviewer(ctx); err != nil {
|
||||||
|
log.Error("Error in PullReviewDismiss while loading reviewer for issue[%d], review[%d] and reviewer[%d]: %v", review.Issue.ID, comment.Review.ID, comment.Review.ReviewerID, err)
|
||||||
|
}
|
||||||
if err := mailer.MailParticipantsComment(ctx, comment, activities_model.ActionPullReviewDismissed, review.Issue, nil); err != nil {
|
if err := mailer.MailParticipantsComment(ctx, comment, activities_model.ActionPullReviewDismissed, review.Issue, nil); err != nil {
|
||||||
log.Error("MailParticipantsComment: %v", err)
|
log.Error("MailParticipantsComment: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/avatars"
|
"code.gitea.io/gitea/models/avatars"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/cache"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/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"
|
||||||
@@ -34,42 +35,36 @@ type PushCommits struct {
|
|||||||
HeadCommit *PushCommit
|
HeadCommit *PushCommit
|
||||||
CompareURL string
|
CompareURL string
|
||||||
Len int
|
Len int
|
||||||
|
|
||||||
avatars map[string]string
|
|
||||||
emailUsers map[string]*user_model.User
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPushCommits creates a new PushCommits object.
|
// NewPushCommits creates a new PushCommits object.
|
||||||
func NewPushCommits() *PushCommits {
|
func NewPushCommits() *PushCommits {
|
||||||
return &PushCommits{
|
return &PushCommits{}
|
||||||
avatars: make(map[string]string),
|
|
||||||
emailUsers: make(map[string]*user_model.User),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
|
// toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
|
||||||
func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
|
func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
|
||||||
var err error
|
var err error
|
||||||
authorUsername := ""
|
authorUsername := ""
|
||||||
author, ok := pc.emailUsers[commit.AuthorEmail]
|
author, ok := emailUsers[commit.AuthorEmail]
|
||||||
if !ok {
|
if !ok {
|
||||||
author, err = user_model.GetUserByEmail(ctx, commit.AuthorEmail)
|
author, err = user_model.GetUserByEmail(ctx, commit.AuthorEmail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
authorUsername = author.Name
|
authorUsername = author.Name
|
||||||
pc.emailUsers[commit.AuthorEmail] = author
|
emailUsers[commit.AuthorEmail] = author
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
authorUsername = author.Name
|
authorUsername = author.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
committerUsername := ""
|
committerUsername := ""
|
||||||
committer, ok := pc.emailUsers[commit.CommitterEmail]
|
committer, ok := emailUsers[commit.CommitterEmail]
|
||||||
if !ok {
|
if !ok {
|
||||||
committer, err = user_model.GetUserByEmail(ctx, commit.CommitterEmail)
|
committer, err = user_model.GetUserByEmail(ctx, commit.CommitterEmail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: check errors other than email not found.
|
// TODO: check errors other than email not found.
|
||||||
committerUsername = committer.Name
|
committerUsername = committer.Name
|
||||||
pc.emailUsers[commit.CommitterEmail] = committer
|
emailUsers[commit.CommitterEmail] = committer
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
committerUsername = committer.Name
|
committerUsername = committer.Name
|
||||||
@@ -107,11 +102,10 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
|
|||||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||||
var headCommit *api.PayloadCommit
|
var headCommit *api.PayloadCommit
|
||||||
|
|
||||||
if pc.emailUsers == nil {
|
emailUsers := make(map[string]*user_model.User)
|
||||||
pc.emailUsers = make(map[string]*user_model.User)
|
|
||||||
}
|
|
||||||
for i, commit := range pc.Commits {
|
for i, commit := range pc.Commits {
|
||||||
apiCommit, err := pc.toAPIPayloadCommit(ctx, repoPath, repoLink, commit)
|
apiCommit, err := pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -123,7 +117,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
|
|||||||
}
|
}
|
||||||
if pc.HeadCommit != nil && headCommit == nil {
|
if pc.HeadCommit != nil && headCommit == nil {
|
||||||
var err error
|
var err error
|
||||||
headCommit, err = pc.toAPIPayloadCommit(ctx, repoPath, repoLink, pc.HeadCommit)
|
headCommit, err = pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -134,35 +128,21 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
|
|||||||
// AvatarLink tries to match user in database with e-mail
|
// AvatarLink tries to match user in database with e-mail
|
||||||
// in order to show custom avatar, and falls back to general avatar link.
|
// in order to show custom avatar, and falls back to general avatar link.
|
||||||
func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
|
func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
|
||||||
if pc.avatars == nil {
|
|
||||||
pc.avatars = make(map[string]string)
|
|
||||||
}
|
|
||||||
avatar, ok := pc.avatars[email]
|
|
||||||
if ok {
|
|
||||||
return avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
|
size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
|
||||||
|
|
||||||
u, ok := pc.emailUsers[email]
|
v, _ := cache.GetWithContextCache(ctx, "push_commits", email, func() (string, error) {
|
||||||
if !ok {
|
u, err := user_model.GetUserByEmail(ctx, email)
|
||||||
var err error
|
|
||||||
u, err = user_model.GetUserByEmail(ctx, email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(ctx, email, size)
|
|
||||||
if !user_model.IsErrUserNotExist(err) {
|
if !user_model.IsErrUserNotExist(err) {
|
||||||
log.Error("GetUserByEmail: %v", err)
|
log.Error("GetUserByEmail: %v", err)
|
||||||
return ""
|
return "", err
|
||||||
}
|
}
|
||||||
} else {
|
return avatars.GenerateEmailAvatarFastLink(ctx, email, size), nil
|
||||||
pc.emailUsers[email] = u
|
|
||||||
}
|
}
|
||||||
}
|
return u.AvatarLinkWithSize(ctx, size), nil
|
||||||
if u != nil {
|
})
|
||||||
pc.avatars[email] = u.AvatarLinkWithSize(ctx, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pc.avatars[email]
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||||
@@ -189,7 +169,5 @@ func GitToPushCommits(gitCommits []*git.Commit) *PushCommits {
|
|||||||
HeadCommit: nil,
|
HeadCommit: nil,
|
||||||
CompareURL: "",
|
CompareURL: "",
|
||||||
Len: len(commits),
|
Len: len(commits),
|
||||||
avatars: make(map[string]string),
|
|
||||||
emailUsers: make(map[string]*user_model.User),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,11 +103,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
|||||||
assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
|
assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableGravatar(t *testing.T) {
|
func initGravatarSource(t *testing.T) {
|
||||||
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
||||||
err = system_model.Init(db.DefaultContext)
|
err := system_model.Init(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +132,7 @@ func TestPushCommits_AvatarLink(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
enableGravatar(t)
|
initGravatarSource(t)
|
||||||
|
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor),
|
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor),
|
||||||
|
|||||||
@@ -174,9 +174,16 @@ func (s *iniConfigSection) ChildSections() (sections []ConfigSection) {
|
|||||||
return sections
|
return sections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configProviderLoadOptions() ini.LoadOptions {
|
||||||
|
return ini.LoadOptions{
|
||||||
|
KeyValueDelimiterOnWrite: " = ",
|
||||||
|
IgnoreContinuation: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewConfigProviderFromData this function is mainly for testing purpose
|
// NewConfigProviderFromData this function is mainly for testing purpose
|
||||||
func NewConfigProviderFromData(configContent string) (ConfigProvider, error) {
|
func NewConfigProviderFromData(configContent string) (ConfigProvider, error) {
|
||||||
cfg, err := ini.Load(strings.NewReader(configContent))
|
cfg, err := ini.LoadSources(configProviderLoadOptions(), strings.NewReader(configContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -190,7 +197,7 @@ func NewConfigProviderFromData(configContent string) (ConfigProvider, error) {
|
|||||||
// NewConfigProviderFromFile load configuration from file.
|
// NewConfigProviderFromFile load configuration from file.
|
||||||
// NOTE: do not print any log except error.
|
// NOTE: do not print any log except error.
|
||||||
func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvider, error) {
|
func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvider, error) {
|
||||||
cfg := ini.Empty(ini.LoadOptions{KeyValueDelimiterOnWrite: " = "})
|
cfg := ini.Empty(configProviderLoadOptions())
|
||||||
loadedFromEmpty := true
|
loadedFromEmpty := true
|
||||||
|
|
||||||
if file != "" {
|
if file != "" {
|
||||||
@@ -339,6 +346,7 @@ func NewConfigProviderForLocale(source any, others ...any) (ConfigProvider, erro
|
|||||||
iniFile, err := ini.LoadSources(ini.LoadOptions{
|
iniFile, err := ini.LoadSources(ini.LoadOptions{
|
||||||
IgnoreInlineComment: true,
|
IgnoreInlineComment: true,
|
||||||
UnescapeValueCommentSymbols: true,
|
UnescapeValueCommentSymbols: true,
|
||||||
|
IgnoreContinuation: true,
|
||||||
}, source, others...)
|
}, source, others...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to load locale ini: %w", err)
|
return nil, fmt.Errorf("unable to load locale ini: %w", err)
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ key = 123
|
|||||||
secSub := cfg.Section("foo.bar.xxx")
|
secSub := cfg.Section("foo.bar.xxx")
|
||||||
assert.Equal(t, "123", secSub.Key("key").String())
|
assert.Equal(t, "123", secSub.Key("key").String())
|
||||||
})
|
})
|
||||||
|
t.Run("TrailingSlash", func(t *testing.T) {
|
||||||
|
cfg, _ := NewConfigProviderFromData(`
|
||||||
|
[foo]
|
||||||
|
key = E:\
|
||||||
|
xxx = yyy
|
||||||
|
`)
|
||||||
|
sec := cfg.Section("foo")
|
||||||
|
assert.Equal(t, "E:\\", sec.Key("key").String())
|
||||||
|
assert.Equal(t, "yyy", sec.Key("xxx").String())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigProviderHelper(t *testing.T) {
|
func TestConfigProviderHelper(t *testing.T) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
|
|||||||
}
|
}
|
||||||
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
|
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
|
||||||
SessionConfig.CookiePath = AppSubURL + "/" // there was a bug, old code only set CookePath=AppSubURL, no trailing slash
|
SessionConfig.CookiePath = AppSubURL + "/" // there was a bug, old code only set CookePath=AppSubURL, no trailing slash
|
||||||
SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(false)
|
SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(strings.HasPrefix(strings.ToLower(AppURL), "https://"))
|
||||||
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
|
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
|
||||||
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)
|
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)
|
||||||
SessionConfig.Domain = sec.Key("DOMAIN").String()
|
SessionConfig.Domain = sec.Key("DOMAIN").String()
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ type Repository struct {
|
|||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
LanguagesURL string `json:"languages_url"`
|
LanguagesURL string `json:"languages_url"`
|
||||||
HTMLURL string `json:"html_url"`
|
HTMLURL string `json:"html_url"`
|
||||||
|
URL string `json:"url"`
|
||||||
Link string `json:"link"`
|
Link string `json:"link"`
|
||||||
SSHURL string `json:"ssh_url"`
|
SSHURL string `json:"ssh_url"`
|
||||||
CloneURL string `json:"clone_url"`
|
CloneURL string `json:"clone_url"`
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func NewFuncMap() template.FuncMap {
|
|||||||
return setting.AssetVersion
|
return setting.AssetVersion
|
||||||
},
|
},
|
||||||
"DisableGravatar": func(ctx context.Context) bool {
|
"DisableGravatar": func(ctx context.Context) bool {
|
||||||
return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar, setting.GetDefaultDisableGravatar())
|
||||||
},
|
},
|
||||||
"DefaultShowFullName": func() bool {
|
"DefaultShowFullName": func() bool {
|
||||||
return setting.UI.DefaultShowFullName
|
return setting.UI.DefaultShowFullName
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ func CreateTeam(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiTeam, err := convert.ToTeam(ctx, team)
|
apiTeam, err := convert.ToTeam(ctx, team, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.InternalServerError(err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -785,7 +785,7 @@ func CompareDiff(ctx *context.Context) {
|
|||||||
|
|
||||||
ctx.Data["IsRepoToolbarCommits"] = true
|
ctx.Data["IsRepoToolbarCommits"] = true
|
||||||
ctx.Data["IsDiffCompare"] = true
|
ctx.Data["IsDiffCompare"] = true
|
||||||
templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
|
_, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
|
||||||
|
|
||||||
if len(templateErrs) > 0 {
|
if len(templateErrs) > 0 {
|
||||||
ctx.Flash.Warning(renderErrorOfTemplates(ctx, templateErrs), true)
|
ctx.Flash.Warning(renderErrorOfTemplates(ctx, templateErrs), true)
|
||||||
|
|||||||
@@ -804,10 +804,11 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
|
|||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string) map[string]error {
|
// Tries to load and set an issue template. The first return value indicates if a template was loaded.
|
||||||
|
func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string) (bool, map[string]error) {
|
||||||
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
templateCandidates := make([]string, 0, 1+len(possibleFiles))
|
templateCandidates := make([]string, 0, 1+len(possibleFiles))
|
||||||
@@ -870,20 +871,15 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
|
|||||||
ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
|
ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
|
||||||
ctx.Data["Reference"] = template.Ref
|
ctx.Data["Reference"] = template.Ref
|
||||||
ctx.Data["RefEndName"] = git.RefName(template.Ref).ShortName()
|
ctx.Data["RefEndName"] = git.RefName(template.Ref).ShortName()
|
||||||
return templateErrs
|
return true, templateErrs
|
||||||
}
|
}
|
||||||
return templateErrs
|
return false, templateErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssue render creating issue page
|
// NewIssue render creating issue page
|
||||||
func NewIssue(ctx *context.Context) {
|
func NewIssue(ctx *context.Context) {
|
||||||
issueConfig, _ := issue_service.GetTemplateConfigFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
issueConfig, _ := issue_service.GetTemplateConfigFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
||||||
hasTemplates := issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
hasTemplates := issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
||||||
if !issueConfig.BlankIssuesEnabled && hasTemplates {
|
|
||||||
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if blank issues are disabled, just redirect to the "issues/choose" page with these parameters.
|
|
||||||
ctx.Redirect(fmt.Sprintf("%s/issues/new/choose?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
|
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
|
||||||
ctx.Data["PageIsIssueList"] = true
|
ctx.Data["PageIsIssueList"] = true
|
||||||
@@ -930,7 +926,8 @@ func NewIssue(ctx *context.Context) {
|
|||||||
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
|
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
|
||||||
|
|
||||||
_, templateErrs := issue_service.GetTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
_, templateErrs := issue_service.GetTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
|
||||||
if errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates); len(errs) > 0 {
|
templateLoaded, errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
|
||||||
|
if len(errs) > 0 {
|
||||||
for k, v := range errs {
|
for k, v := range errs {
|
||||||
templateErrs[k] = v
|
templateErrs[k] = v
|
||||||
}
|
}
|
||||||
@@ -945,6 +942,12 @@ func NewIssue(ctx *context.Context) {
|
|||||||
|
|
||||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(unit.TypeIssues)
|
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(unit.TypeIssues)
|
||||||
|
|
||||||
|
if !issueConfig.BlankIssuesEnabled && hasTemplates && !templateLoaded {
|
||||||
|
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if blank issues are disabled, just redirect to the "issues/choose" page with these parameters.
|
||||||
|
ctx.Redirect(fmt.Sprintf("%s/issues/new/choose?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplIssueNew)
|
ctx.HTML(http.StatusOK, tplIssueNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,10 +223,10 @@ func Profile(ctx *context.Context) {
|
|||||||
switch tab {
|
switch tab {
|
||||||
case "followers":
|
case "followers":
|
||||||
ctx.Data["Cards"] = followers
|
ctx.Data["Cards"] = followers
|
||||||
total = int(count)
|
total = int(numFollowers)
|
||||||
case "following":
|
case "following":
|
||||||
ctx.Data["Cards"] = following
|
ctx.Data["Cards"] = following
|
||||||
total = int(count)
|
total = int(numFollowing)
|
||||||
case "activity":
|
case "activity":
|
||||||
date := ctx.FormString("date")
|
date := ctx.FormString("date")
|
||||||
items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
|
items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
|
||||||
|
|||||||
@@ -863,9 +863,6 @@ func registerRoutes(m *web.Route) {
|
|||||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false))
|
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false))
|
||||||
}, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
|
}, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
|
||||||
|
|
||||||
// ***** Release Attachment Download without Signin
|
|
||||||
m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload)
|
|
||||||
|
|
||||||
m.Group("/{username}/{reponame}", func() {
|
m.Group("/{username}/{reponame}", func() {
|
||||||
m.Group("/settings", func() {
|
m.Group("/settings", func() {
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
@@ -1118,8 +1115,9 @@ func registerRoutes(m *web.Route) {
|
|||||||
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
|
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
|
||||||
m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
|
m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
|
||||||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
|
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
|
||||||
repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag, true))
|
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true))
|
||||||
m.Get("/releases/attachments/{uuid}", repo.MustBeNotEmpty, reqRepoReleaseReader, repo.GetAttachment)
|
m.Get("/releases/attachments/{uuid}", repo.MustBeNotEmpty, repo.GetAttachment)
|
||||||
|
m.Get("/releases/download/{vTag}/{fileName}", repo.MustBeNotEmpty, repo.RedirectDownload)
|
||||||
m.Group("/releases", func() {
|
m.Group("/releases", func() {
|
||||||
m.Get("/new", repo.NewRelease)
|
m.Get("/new", repo.NewRelease)
|
||||||
m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost)
|
m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost)
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ func (o *OAuth2) userIDFromToken(tokenSHA string, store DataStore) int64 {
|
|||||||
// If verification is successful returns an existing user object.
|
// If verification is successful returns an existing user object.
|
||||||
// Returns nil if verification fails.
|
// Returns nil if verification fails.
|
||||||
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) {
|
// These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
|
||||||
|
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
|
||||||
|
!gitRawReleasePathRe.MatchString(req.URL.Path) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
|||||||
Parent: parent,
|
Parent: parent,
|
||||||
Mirror: repo.IsMirror,
|
Mirror: repo.IsMirror,
|
||||||
HTMLURL: repo.HTMLURL(),
|
HTMLURL: repo.HTMLURL(),
|
||||||
|
URL: repoAPIURL,
|
||||||
SSHURL: cloneLink.SSH,
|
SSHURL: cloneLink.SSH,
|
||||||
CloneURL: cloneLink.HTTPS,
|
CloneURL: cloneLink.HTTPS,
|
||||||
OriginalURL: repo.SanitizedOriginalURL(),
|
OriginalURL: repo.SanitizedOriginalURL(),
|
||||||
|
|||||||
@@ -1312,7 +1312,7 @@ outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff, err
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommentAsDiff returns c.Patch as *Diff
|
// CommentAsDiff returns c.Patch as *Diff
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ func (d *DingtalkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e
|
|||||||
func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
||||||
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
|
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
|
||||||
|
|
||||||
return createDingtalkPayload(text, text, "view release", p.Release.URL), nil
|
return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload {
|
func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload {
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func TestDingTalkPayload(t *testing.T) {
|
|||||||
assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Text)
|
assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Text)
|
||||||
assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Title)
|
assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Title)
|
||||||
assert.Equal(t, "view release", pl.(*DingtalkPayload).ActionCard.SingleTitle)
|
assert.Equal(t, "view release", pl.(*DingtalkPayload).ActionCard.SingleTitle)
|
||||||
assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
|
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ func (d *DiscordPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
|
|||||||
func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
||||||
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
||||||
|
|
||||||
return d.createPayload(p.Sender, text, p.Release.Note, p.Release.URL, color), nil
|
return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDiscordPayload converts a discord webhook into a DiscordPayload
|
// GetDiscordPayload converts a discord webhook into a DiscordPayload
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ func TestDiscordPayload(t *testing.T) {
|
|||||||
assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
|
assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
|
||||||
assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*DiscordPayload).Embeds[0].Title)
|
assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*DiscordPayload).Embeds[0].Title)
|
||||||
assert.Equal(t, "Note of first stable release", pl.(*DiscordPayload).Embeds[0].Description)
|
assert.Equal(t, "Note of first stable release", pl.(*DiscordPayload).Embeds[0].Description)
|
||||||
assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", pl.(*DiscordPayload).Embeds[0].URL)
|
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.(*DiscordPayload).Embeds[0].URL)
|
||||||
assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
|
assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
|
||||||
assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
|
assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
|
||||||
assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
|
assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ func pullReleaseTestPayload() *api.ReleasePayload {
|
|||||||
Target: "master",
|
Target: "master",
|
||||||
Title: "First stable release",
|
Title: "First stable release",
|
||||||
Note: "Note of first stable release",
|
Note: "Note of first stable release",
|
||||||
URL: "http://localhost:3000/api/v1/repos/test/repo/releases/2",
|
HTMLURL: "http://localhost:3000/test/repo/releases/tag/v1.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ func (m *MatrixPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, e
|
|||||||
func (m *MatrixPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) {
|
func (m *MatrixPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) {
|
||||||
senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
|
senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
|
||||||
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||||
titleLink := MatrixLinkFormatter(p.PullRequest.URL, title)
|
titleLink := MatrixLinkFormatter(p.PullRequest.HTMLURL, title)
|
||||||
repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||||
var text string
|
var text string
|
||||||
|
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
|||||||
p.Sender,
|
p.Sender,
|
||||||
title,
|
title,
|
||||||
"",
|
"",
|
||||||
p.Release.URL,
|
p.Release.HTMLURL,
|
||||||
color,
|
color,
|
||||||
&MSTeamsFact{"Tag:", p.Release.TagName},
|
&MSTeamsFact{"Tag:", p.Release.TagName},
|
||||||
), nil
|
), nil
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ func TestMSTeamsPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
|
assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
|
||||||
assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
|
assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
|
||||||
assert.Equal(t, "http://localhost:3000/api/v1/repos/test/repo/releases/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
|
assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ func (s *SlackPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, er
|
|||||||
attachments = append(attachments, SlackAttachment{
|
attachments = append(attachments, SlackAttachment{
|
||||||
Color: fmt.Sprintf("%x", color),
|
Color: fmt.Sprintf("%x", color),
|
||||||
Title: issueTitle,
|
Title: issueTitle,
|
||||||
TitleLink: p.PullRequest.URL,
|
TitleLink: p.PullRequest.HTMLURL,
|
||||||
Text: attachmentText,
|
Text: attachmentText,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,8 +249,8 @@ func TestPrepareWikiFileName(t *testing.T) {
|
|||||||
unittest.PrepareTestEnv(t)
|
unittest.PrepareTestEnv(t)
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
|
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
|
||||||
defer gitRepo.Close()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -301,8 +301,8 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir)
|
gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir)
|
||||||
defer gitRepo.Close()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
existence, newWikiPath, err := prepareGitPath(gitRepo, "Home")
|
existence, newWikiPath, err := prepareGitPath(gitRepo, "Home")
|
||||||
assert.False(t, existence)
|
assert.False(t, existence)
|
||||||
|
|||||||
4
templates/swagger/v1_json.tmpl
generated
4
templates/swagger/v1_json.tmpl
generated
@@ -21038,6 +21038,10 @@
|
|||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"x-go-name": "Updated"
|
"x-go-name": "Updated"
|
||||||
},
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "URL"
|
||||||
|
},
|
||||||
"watchers_count": {
|
"watchers_count": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
1032bbf17fbc0d9c95bb5418dabe8f8c99278700
|
||||||
@@ -239,3 +239,20 @@ func TestViewTagsList(t *testing.T) {
|
|||||||
|
|
||||||
assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
|
assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDownloadReleaseAttachment(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
tests.PrepareAttachmentsStorage(t)
|
||||||
|
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||||
|
|
||||||
|
url := repo.Link() + "/releases/download/v1.1/README.md"
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", url)
|
||||||
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
}
|
||||||
|
|||||||
@@ -176,6 +176,20 @@ func InitTest(requireGitea bool) {
|
|||||||
routers.InitWebInstalled(graceful.GetManager().HammerContext())
|
routers.InitWebInstalled(graceful.GetManager().HammerContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PrepareAttachmentsStorage(t testing.TB) {
|
||||||
|
// prepare attachments directory and files
|
||||||
|
assert.NoError(t, storage.Clean(storage.Attachments))
|
||||||
|
|
||||||
|
s, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
|
||||||
|
Path: filepath.Join(filepath.Dir(setting.AppPath), "tests", "testdata", "data", "attachments"),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, s.IterateObjects("", func(p string, obj storage.Object) error {
|
||||||
|
_, err = storage.Copy(storage.Attachments, p, s, p)
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
ourSkip := 2
|
ourSkip := 2
|
||||||
|
|||||||
1
tests/testdata/data/attachments/a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22
vendored
Normal file
1
tests/testdata/data/attachments/a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# This is a release README
|
||||||
@@ -86,6 +86,7 @@ text-expander .suggestions {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid var(--color-secondary);
|
border: 1px solid var(--color-secondary);
|
||||||
box-shadow: 0 .5rem 1rem var(--color-shadow);
|
box-shadow: 0 .5rem 1rem var(--color-shadow);
|
||||||
|
z-index: 100; /* needs to be > 20 to be on top of dropzone's .dz-details */
|
||||||
}
|
}
|
||||||
|
|
||||||
text-expander .suggestions li {
|
text-expander .suggestions li {
|
||||||
|
|||||||
Reference in New Issue
Block a user