mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-21 00:01:16 -04:00
Compare commits
5 Commits
993178b45f
...
aabcf2d7ad
Author | SHA1 | Date | |
---|---|---|---|
|
aabcf2d7ad | ||
|
6919a02ab7 | ||
|
1848858a1e | ||
|
a398089301 | ||
|
8885108c42 |
70
modules/doctor/repository.go
Normal file
70
modules/doctor/repository.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package doctor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||||
|
test := &consistencyCheck{
|
||||||
|
Name: "Repos with no existing owner",
|
||||||
|
Counter: countOrphanedRepos,
|
||||||
|
Fixer: deleteOrphanedRepos,
|
||||||
|
FixedMessage: "Deleted all content related to orphaned repos",
|
||||||
|
}
|
||||||
|
return test.Run(ctx, logger, autofix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// countOrphanedRepos count repository where user of owner_id do not exist
|
||||||
|
func countOrphanedRepos(ctx context.Context) (int64, error) {
|
||||||
|
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteOrphanedRepos delete repository where user of owner_id do not exist
|
||||||
|
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
|
||||||
|
batchSize := db.MaxBatchInsertSize("repository")
|
||||||
|
e := db.GetEngine(ctx)
|
||||||
|
var deleted int64
|
||||||
|
adminUser := &user_model.User{IsAdmin: true}
|
||||||
|
|
||||||
|
for {
|
||||||
|
var ids []int64
|
||||||
|
if err := e.Table("`repository`").
|
||||||
|
Join("LEFT", "`user`", "repository.owner_id=user.id").
|
||||||
|
Where(builder.IsNull{"`user`.id"}).
|
||||||
|
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
|
||||||
|
return deleted, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't get ids we have deleted them all
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return deleted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, 0, id, true); err != nil {
|
||||||
|
return deleted, err
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register(&Check{
|
||||||
|
Title: "Deleted all content related to orphaned repos",
|
||||||
|
Name: "delete-orphaned-repos",
|
||||||
|
IsDefault: false,
|
||||||
|
Run: handleDeleteOrphanedRepos,
|
||||||
|
Priority: 4,
|
||||||
|
})
|
||||||
|
}
|
@ -74,27 +74,31 @@ func ActionIcon(opType activities_model.ActionType) string {
|
|||||||
switch opType {
|
switch opType {
|
||||||
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
|
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
|
||||||
return "repo"
|
return "repo"
|
||||||
case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
|
case activities_model.ActionCommitRepo:
|
||||||
return "git-commit"
|
return "git-commit"
|
||||||
case activities_model.ActionCreateIssue:
|
case activities_model.ActionDeleteBranch:
|
||||||
return "issue-opened"
|
return "git-branch"
|
||||||
case activities_model.ActionCreatePullRequest:
|
|
||||||
return "git-pull-request"
|
|
||||||
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
|
|
||||||
return "comment-discussion"
|
|
||||||
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
|
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
|
||||||
return "git-merge"
|
return "git-merge"
|
||||||
case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
|
case activities_model.ActionCreatePullRequest:
|
||||||
|
return "git-pull-request"
|
||||||
|
case activities_model.ActionClosePullRequest:
|
||||||
|
return "git-pull-request-closed"
|
||||||
|
case activities_model.ActionCreateIssue:
|
||||||
|
return "issue-opened"
|
||||||
|
case activities_model.ActionCloseIssue:
|
||||||
return "issue-closed"
|
return "issue-closed"
|
||||||
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
|
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
|
||||||
return "issue-reopened"
|
return "issue-reopened"
|
||||||
|
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
|
||||||
|
return "comment-discussion"
|
||||||
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
|
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
|
||||||
return "mirror"
|
return "mirror"
|
||||||
case activities_model.ActionApprovePullRequest:
|
case activities_model.ActionApprovePullRequest:
|
||||||
return "check"
|
return "check"
|
||||||
case activities_model.ActionRejectPullRequest:
|
case activities_model.ActionRejectPullRequest:
|
||||||
return "diff"
|
return "file-diff"
|
||||||
case activities_model.ActionPublishRelease:
|
case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
|
||||||
return "tag"
|
return "tag"
|
||||||
case activities_model.ActionPullReviewDismissed:
|
case activities_model.ActionPullReviewDismissed:
|
||||||
return "x"
|
return "x"
|
||||||
|
@ -1443,10 +1443,10 @@ func Routes() *web.Route {
|
|||||||
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
||||||
})
|
})
|
||||||
m.Group("/teams", func() {
|
m.Group("/teams", func() {
|
||||||
m.Get("", reqToken(), org.ListTeams)
|
m.Get("", org.ListTeams)
|
||||||
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam)
|
m.Post("", reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam)
|
||||||
m.Get("/search", reqToken(), org.SearchTeam)
|
m.Get("/search", org.SearchTeam)
|
||||||
}, reqOrgMembership())
|
}, reqToken(), reqOrgMembership())
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
m.Get("", org.ListLabels)
|
m.Get("", org.ListLabels)
|
||||||
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel)
|
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel)
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
|
|
||||||
// DeleteRepository deletes a repository for a user or organization.
|
// DeleteRepository deletes a repository for a user or organization.
|
||||||
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
|
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
|
||||||
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, repoID int64) error {
|
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, repoID int64, ignoreOrgTeams ...bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -53,10 +53,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, r
|
|||||||
return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err)
|
return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case is a organization.
|
// In case owner is a organization, we have to change repo specific teams
|
||||||
org, err := user_model.GetUserByID(ctx, uid)
|
// if ignoreOrgTeams is not true
|
||||||
if err != nil {
|
var org *user_model.User
|
||||||
return err
|
if len(ignoreOrgTeams) == 0 || !ignoreOrgTeams[0] {
|
||||||
|
if org, err = user_model.GetUserByID(ctx, uid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := &repo_model.Repository{OwnerID: uid}
|
repo := &repo_model.Repository{OwnerID: uid}
|
||||||
@ -95,7 +98,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if org.IsOrganization() {
|
if org != nil && org.IsOrganization() {
|
||||||
teams, err := organization.FindOrgTeams(ctx, org.ID)
|
teams, err := organization.FindOrgTeams(ctx, org.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -78,20 +78,23 @@
|
|||||||
{{$reviewer := index .GetIssueInfos 1}}
|
{{$reviewer := index .GetIssueInfos 1}}
|
||||||
{{ctx.Locale.Tr "action.review_dismissed" ((printf "%s/pulls/%s" (.GetRepoLink ctx) $index) |Escape) $index ((.ShortRepoPath ctx)|Escape) $reviewer | Str2html}}
|
{{ctx.Locale.Tr "action.review_dismissed" ((printf "%s/pulls/%s" (.GetRepoLink ctx) $index) |Escape) $index ((.ShortRepoPath ctx)|Escape) $reviewer | Str2html}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{TimeSince .GetCreate ctx.Locale}}
|
||||||
</div>
|
</div>
|
||||||
{{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}}
|
{{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}}
|
||||||
{{$push := ActionContent2Commits .}}
|
{{$push := ActionContent2Commits .}}
|
||||||
{{$repoLink := (.GetRepoLink ctx)}}
|
{{$repoLink := (.GetRepoLink ctx)}}
|
||||||
{{range $push.Commits}}
|
<div class="gt-df gt-fc gt-gap-2">
|
||||||
{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
|
{{range $push.Commits}}
|
||||||
<div class="flex-text-block">
|
{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
|
||||||
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
|
<div class="flex-text-block">
|
||||||
<a class="gt-mono" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
|
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
|
||||||
<span class="text truncate light grey">
|
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
|
||||||
{{RenderCommitMessage $.Context .Message $repoLink $.ComposeMetas}}
|
<span class="text truncate">
|
||||||
</span>
|
{{RenderCommitMessage $.Context .Message $repoLink $.ComposeMetas}}
|
||||||
</div>
|
</span>
|
||||||
{{end}}
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
{{if and (gt $push.Len 1) $push.CompareURL}}
|
{{if and (gt $push.Len 1) $push.CompareURL}}
|
||||||
<a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{ctx.Locale.Tr "action.compare_commits" $push.Len}} »</a>
|
<a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{ctx.Locale.Tr "action.compare_commits" $push.Len}} »</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -103,17 +106,16 @@
|
|||||||
<a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
<a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</a>
|
||||||
{{$comment := index .GetIssueInfos 1}}
|
{{$comment := index .GetIssueInfos 1}}
|
||||||
{{if gt (len $comment) 0}}
|
{{if gt (len $comment) 0}}
|
||||||
<div class="flex-item-body">{{$comment | RenderEmoji $.Context | RenderCodeBlock}}</div>
|
<div class="markup gt-font-14">{{RenderMarkdownToHtml ctx $comment}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if .GetOpType.InActions "merge_pull_request"}}
|
{{else if .GetOpType.InActions "merge_pull_request"}}
|
||||||
<div class="flex-item-body">{{index .GetIssueInfos 1}}</div>
|
<div class="flex-item-body text black">{{index .GetIssueInfos 1}}</div>
|
||||||
{{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}}
|
{{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}}
|
||||||
<span class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</span>
|
<span class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</span>
|
||||||
{{else if .GetOpType.InActions "pull_review_dismissed"}}
|
{{else if .GetOpType.InActions "pull_review_dismissed"}}
|
||||||
<div class="flex-item-body">{{ctx.Locale.Tr "action.review_dismissed_reason"}}</div>
|
<div class="flex-item-body text black">{{ctx.Locale.Tr "action.review_dismissed_reason"}}</div>
|
||||||
<div class="flex-item-body">{{index .GetIssueInfos 2 | RenderEmoji $.Context}}</div>
|
<div class="flex-item-body text black">{{index .GetIssueInfos 2 | RenderEmoji $.Context}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="flex-item-body">{{TimeSince .GetCreate ctx.Locale}}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-trailing">
|
<div class="flex-item-trailing">
|
||||||
{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32 "text grey gt-mr-2"}}
|
{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32 "text grey gt-mr-2"}}
|
||||||
|
@ -69,7 +69,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flex-item .flex-item-body {
|
.flex-item .flex-item-body {
|
||||||
font-size: 13px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -126,7 +126,7 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="store.fileTreeIsVisible" class="gt-mr-3">
|
<div v-if="store.fileTreeIsVisible" class="diff-file-tree-items">
|
||||||
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
|
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
|
||||||
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
|
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
|
||||||
<div v-if="store.isIncomplete" class="gt-pt-2">
|
<div v-if="store.isIncomplete" class="gt-pt-2">
|
||||||
@ -134,3 +134,11 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.diff-file-tree-items {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
margin-right: .5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -58,11 +58,14 @@ a, a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sub-items {
|
.sub-items {
|
||||||
padding-left: 9px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-file {
|
.sub-items .item-file {
|
||||||
margin-left: 20px;
|
padding-left: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-file.selected {
|
.item-file.selected {
|
||||||
@ -80,7 +83,7 @@ a, a:hover {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
padding: 2px;
|
padding: 3px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-file:hover,
|
.item-file:hover,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user