Compare commits

...

7 Commits

Author SHA1 Message Date
Sybren
151b1a9508
Support importing comment types (#22510)
This commit adds support for specifying comment types when importing
with `gitea restore-repo`. It makes it possible to import issue changes,
such as "title changed" or "assigned user changed".

An earlier version of this pull request was made by Matti Ranta, in
https://future.projects.blender.org/blender-migration/gitea-bf/pulls/3

There are two changes with regard to Matti's original code:

1. The comment type was an `int64` in Matti's code, and is now using a
string. This makes it possible to use `comment_type: title`, which is
more reliable and future-proof than an index into an internal list in
the Gitea Go code.

2. Matti's code also had support for including labels, but in a way that
would require knowing the database ID of the labels before the import
even starts, which is impossible. This can be solved by using label
names instead of IDs; for simplicity I I left that out of this PR.
2023-01-18 21:14:56 -05:00
Brecht Van Lommel
cdf53fa4a7
Fix issue not auto-closing when it includes a reference to a branch (#22514)
Ensure branch prefix is stripped away for both when comparing the branch
name.
2023-01-18 19:24:38 -05:00
Brecht Van Lommel
e0a8965208
Fix invalid issue branch reference if not specified in template (#22513)
When an issue template does not contain a ref, it would end up with an
invalid `ref/heads/` value instead of having no branch referenced .
2023-01-18 16:50:22 -05:00
Brecht Van Lommel
aa87b36900
Fix 500 error viewing pull request when fork has pull requests disabled (#22512)
Swallow error just like in #20839, for the case where there is no
protected branch.

Fixes #20826 for me, though I can't tell if this now covers all cases.
2023-01-18 13:54:57 -06:00
Jason Song
4804900ac9
Load asciicast css async (#22502)
Load asciicast css asynchronously.

Related to #22448.
2023-01-18 12:19:38 -06:00
Sybren
326d29dce0
Reliable selection of admin user (#22509)
When importing a repository via `gitea restore-repo`, external users
will get remapped to an admin user. This admin user is obtained via
`users.GetAdminUser()`, which unfortunately picks a more-or-less random
admin to return.

This makes it hard to predict which admin user will get assigned. This
patch orders the admin by ascending ID before choosing the first one,
i.e. it picks the admin with the lowest ID.

Even though it would be nicer to have full control over which user is
chosen, this at least gives us a predictable result.
2023-01-18 10:57:16 -06:00
Lunny Xiao
f59ce77772
Display unreferenced packages total size in package admin panel (#22498) 2023-01-18 09:52:04 -06:00
16 changed files with 84 additions and 18 deletions

View File

@ -175,6 +175,15 @@ func (t CommentType) String() string {
return commentStrings[t] return commentStrings[t]
} }
func AsCommentType(typeName string) CommentType {
for index, name := range commentStrings {
if typeName == name {
return CommentType(index)
}
}
return CommentTypeUnknown
}
// RoleDescriptor defines comment tag type // RoleDescriptor defines comment tag type
type RoleDescriptor int type RoleDescriptor int

View File

@ -62,3 +62,10 @@ func TestFetchCodeComments(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, res, 1) assert.Len(t, res, 1)
} }
func TestAsCommentType(t *testing.T) {
assert.Equal(t, issues_model.CommentTypeUnknown, issues_model.AsCommentType(""))
assert.Equal(t, issues_model.CommentTypeUnknown, issues_model.AsCommentType("nonsense"))
assert.Equal(t, issues_model.CommentTypeComment, issues_model.AsCommentType("comment"))
assert.Equal(t, issues_model.CommentTypePRUnScheduledToAutoMerge, issues_model.AsCommentType("pull_cancel_scheduled_merge"))
}

View File

@ -85,7 +85,16 @@ func DeleteBlobByID(ctx context.Context, blobID int64) error {
} }
// GetTotalBlobSize returns the total blobs size in bytes // GetTotalBlobSize returns the total blobs size in bytes
func GetTotalBlobSize() (int64, error) { func GetTotalBlobSize(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext). return db.GetEngine(ctx).
SumInt(&PackageBlob{}, "size")
}
// GetTotalUnreferencedBlobSize returns the total size of all unreferenced blobs in bytes
func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) {
return db.GetEngine(ctx).
Table("package_blob").
Join("LEFT", "package_file", "package_file.blob_id = package_blob.id").
Where("package_file.id IS NULL").
SumInt(&PackageBlob{}, "size") SumInt(&PackageBlob{}, "size")
} }

View File

@ -199,9 +199,9 @@ func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*Packag
return pfs, count, err return pfs, count, err
} }
// CalculateBlobSize sums up all blob sizes matching the search options. // CalculateFileSize sums up all blob sizes matching the search options.
// It does NOT respect the deduplication of blobs. // It does NOT respect the deduplication of blobs.
func CalculateBlobSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) { func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) {
return db.GetEngine(ctx). return db.GetEngine(ctx).
Table("package_file"). Table("package_file").
Where(opts.toConds()). Where(opts.toConds()).

View File

@ -1233,7 +1233,10 @@ func GetUserByOpenID(uri string) (*User, error) {
// GetAdminUser returns the first administrator // GetAdminUser returns the first administrator
func GetAdminUser() (*User, error) { func GetAdminUser() (*User, error) {
var admin User var admin User
has, err := db.GetEngine(db.DefaultContext).Where("is_admin=?", true).Get(&admin) has, err := db.GetEngine(db.DefaultContext).
Where("is_admin=?", true).
Asc("id"). // Reliably get the admin with the lowest ID.
Get(&admin)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {

View File

@ -17,6 +17,7 @@ type Commentable interface {
type Comment struct { type Comment struct {
IssueIndex int64 `yaml:"issue_index"` IssueIndex int64 `yaml:"issue_index"`
Index int64 Index int64
CommentType string `yaml:"comment_type"` // see `commentStrings` in models/issues/comment.go
PosterID int64 `yaml:"poster_id"` PosterID int64 `yaml:"poster_id"`
PosterName string `yaml:"poster_name"` PosterName string `yaml:"poster_name"`
PosterEmail string `yaml:"poster_email"` PosterEmail string `yaml:"poster_email"`
@ -24,6 +25,7 @@ type Comment struct {
Updated time.Time Updated time.Time
Content string Content string
Reactions []*Reaction Reactions []*Reaction
Meta map[string]interface{} `yaml:"meta,omitempty"` // see models/issues/comment.go for fields in Comment struct
} }
// GetExternalName ExternalUserMigrated interface // GetExternalName ExternalUserMigrated interface

View File

@ -2645,6 +2645,7 @@ repos.size = Size
packages.package_manage_panel = Package Management packages.package_manage_panel = Package Management
packages.total_size = Total Size: %s packages.total_size = Total Size: %s
packages.unreferenced_size = Unreferenced Size: %s
packages.owner = Owner packages.owner = Owner
packages.creator = Creator packages.creator = Creator
packages.name = Name packages.name = Name

View File

@ -51,12 +51,18 @@ func Packages(ctx *context.Context) {
return return
} }
totalBlobSize, err := packages_model.GetTotalBlobSize() totalBlobSize, err := packages_model.GetTotalBlobSize(ctx)
if err != nil { if err != nil {
ctx.ServerError("GetTotalBlobSize", err) ctx.ServerError("GetTotalBlobSize", err)
return return
} }
totalUnreferencedBlobSize, err := packages_model.GetTotalUnreferencedBlobSize(ctx)
if err != nil {
ctx.ServerError("CalculateBlobSize", err)
return
}
ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminPackages"] = true ctx.Data["PageIsAdminPackages"] = true
@ -65,8 +71,9 @@ func Packages(ctx *context.Context) {
ctx.Data["AvailableTypes"] = packages_model.TypeList ctx.Data["AvailableTypes"] = packages_model.TypeList
ctx.Data["SortType"] = sort ctx.Data["SortType"] = sort
ctx.Data["PackageDescriptors"] = pds ctx.Data["PackageDescriptors"] = pds
ctx.Data["Total"] = total ctx.Data["TotalCount"] = total
ctx.Data["TotalBlobSize"] = totalBlobSize ctx.Data["TotalBlobSize"] = totalBlobSize - totalUnreferencedBlobSize
ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamString("q", query) pager.AddParamString("q", query)

View File

@ -784,7 +784,8 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
} }
} }
if !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/<ref>
if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/<ref>
template.Ref = git.BranchPrefix + template.Ref template.Ref = git.BranchPrefix + template.Ref
} }
ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0 ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0

View File

@ -18,6 +18,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/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
@ -175,7 +176,8 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm
if !repo.CloseIssuesViaCommitInAnyBranch { if !repo.CloseIssuesViaCommitInAnyBranch {
// If the issue was specified to be in a particular branch, don't allow commits in other branches to close it // If the issue was specified to be in a particular branch, don't allow commits in other branches to close it
if refIssue.Ref != "" { if refIssue.Ref != "" {
if branchName != refIssue.Ref { issueBranchName := strings.TrimPrefix(refIssue.Ref, git.BranchPrefix)
if branchName != issueBranchName {
continue continue
} }
// Otherwise, only process commits to the default branch // Otherwise, only process commits to the default branch

View File

@ -454,15 +454,34 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
if comment.Updated.IsZero() { if comment.Updated.IsZero() {
comment.Updated = comment.Created comment.Updated = comment.Created
} }
if comment.CommentType == "" {
// if type field is missing, then assume a normal comment
comment.CommentType = issues_model.CommentTypeComment.String()
}
cm := issues_model.Comment{ cm := issues_model.Comment{
IssueID: issue.ID, IssueID: issue.ID,
Type: issues_model.CommentTypeComment, Type: issues_model.AsCommentType(comment.CommentType),
Content: comment.Content, Content: comment.Content,
CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()), CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()),
UpdatedUnix: timeutil.TimeStamp(comment.Updated.Unix()), UpdatedUnix: timeutil.TimeStamp(comment.Updated.Unix()),
} }
switch cm.Type {
case issues_model.CommentTypeAssignees:
cm.AssigneeID = comment.Meta["AssigneeID"].(int64)
if comment.Meta["RemovedAssigneeID"] != nil {
cm.RemovedAssignee = true
}
case issues_model.CommentTypeChangeTitle:
if comment.Meta["OldTitle"] != nil {
cm.OldTitle = fmt.Sprintf("%s", comment.Meta["OldTitle"])
}
if comment.Meta["NewTitle"] != nil {
cm.NewTitle = fmt.Sprintf("%s", comment.Meta["NewTitle"])
}
default:
}
if err := g.remapUser(comment, &cm); err != nil { if err := g.remapUser(comment, &cm); err != nil {
return err return err
} }

View File

@ -361,11 +361,11 @@ func checkSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p
} }
if setting.Packages.LimitTotalOwnerSize > -1 { if setting.Packages.LimitTotalOwnerSize > -1 {
totalSize, err := packages_model.CalculateBlobSize(ctx, &packages_model.PackageFileSearchOptions{ totalSize, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
OwnerID: owner.ID, OwnerID: owner.ID,
}) })
if err != nil { if err != nil {
log.Error("CalculateBlobSize failed: %v", err) log.Error("CalculateFileSize failed: %v", err)
return err return err
} }
if totalSize+uploadSize > setting.Packages.LimitTotalOwnerSize { if totalSize+uploadSize > setting.Packages.LimitTotalOwnerSize {

View File

@ -118,6 +118,9 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
} }
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
if err != nil { if err != nil {
if repo_model.IsErrUnitTypeNotExist(err) {
return false, false, nil
}
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
return false, false, err return false, false, err
} }

View File

@ -4,7 +4,9 @@
<div class="ui container"> <div class="ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .Total}}, {{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}}) {{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}},
{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<form class="ui form ignore-dirty"> <form class="ui form ignore-dirty">

View File

@ -2,7 +2,10 @@ export async function renderAsciinemaPlayer() {
const els = document.querySelectorAll('.asciinema-player-container'); const els = document.querySelectorAll('.asciinema-player-container');
if (!els.length) return; if (!els.length) return;
const player = await import(/* webpackChunkName: "asciinema-player" */'asciinema-player'); const [player] = await Promise.all([
import(/* webpackChunkName: "asciinema-player" */'asciinema-player'),
import(/* webpackChunkName: "asciinema-player" */'asciinema-player/dist/bundle/asciinema-player.css'),
]);
for (const el of els) { for (const el of els) {
player.create(el.getAttribute('data-asciinema-player-src'), el, { player.create(el.getAttribute('data-asciinema-player-src'), el, {

View File

@ -1,5 +1,3 @@
@import "../asciinema-player/dist/bundle/asciinema-player.css";
.asciinema-player-container { .asciinema-player-container {
width: 100%; width: 100%;
height: auto; height: auto;