2014-02-14 16:16:54 -07:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-08-26 07:57:41 -06:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2014-02-14 16:16:54 -07:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-14 07:20:57 -07:00
package models
import (
2019-12-15 02:51:28 -07:00
"context"
2014-03-10 18:48:58 -06:00
"fmt"
2019-04-22 14:40:51 -06:00
"strconv"
2014-02-14 07:20:57 -07:00
2021-11-17 05:34:35 -07:00
_ "image/jpeg" // Needed for jpeg support
2021-11-17 22:58:42 -07:00
admin_model "code.gitea.io/gitea/models/admin"
2021-12-10 01:14:24 -07:00
asymkey_model "code.gitea.io/gitea/models/asymkey"
2021-09-19 05:49:59 -06:00
"code.gitea.io/gitea/models/db"
2022-04-08 03:11:15 -06:00
issues_model "code.gitea.io/gitea/models/issues"
2022-03-29 00:29:02 -06:00
"code.gitea.io/gitea/models/organization"
2021-11-28 04:58:28 -07:00
"code.gitea.io/gitea/models/perm"
2022-05-11 04:09:36 -06:00
access_model "code.gitea.io/gitea/models/perm/access"
2022-03-29 08:16:31 -06:00
project_model "code.gitea.io/gitea/models/project"
2021-11-19 06:39:57 -07:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 12:57:58 -07:00
"code.gitea.io/gitea/models/unit"
2021-11-24 02:49:20 -07:00
user_model "code.gitea.io/gitea/models/user"
2021-11-09 22:13:16 -07:00
"code.gitea.io/gitea/models/webhook"
2021-04-08 16:25:57 -06:00
"code.gitea.io/gitea/modules/lfs"
2016-11-10 09:24:48 -07:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2020-08-17 22:23:45 -06:00
"code.gitea.io/gitea/modules/storage"
2019-05-11 04:21:34 -06:00
api "code.gitea.io/gitea/modules/structs"
2019-11-10 14:33:47 -07:00
"code.gitea.io/gitea/modules/util"
2017-01-01 11:15:09 -07:00
2020-06-13 05:35:59 -06:00
"xorm.io/builder"
2014-02-14 07:20:57 -07:00
)
2022-03-29 01:23:45 -06:00
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
var ItemsPerPage = 40
2014-03-17 09:56:50 -06:00
2016-11-28 10:27:55 -07:00
// NewRepoContext creates a new repository context
2014-03-20 23:48:10 -06:00
func NewRepoContext ( ) {
2021-11-09 12:57:58 -07:00
unit . LoadUnitConfig ( )
2014-03-10 23:32:36 -06:00
}
2021-12-09 18:27:50 -07:00
// CheckRepoUnitUser check whether user could visit the unit of this repository
2022-06-06 02:01:49 -06:00
func CheckRepoUnitUser ( ctx context . Context , repo * repo_model . Repository , user * user_model . User , unitType unit . Type ) bool {
2022-05-05 21:54:22 -06:00
if user != nil && user . IsAdmin {
2018-11-28 04:26:14 -07:00
return true
2017-06-14 20:50:12 -06:00
}
2022-05-11 04:09:36 -06:00
perm , err := access_model . GetUserRepoPermission ( ctx , repo , user )
2018-11-28 04:26:14 -07:00
if err != nil {
2022-04-28 05:48:48 -06:00
log . Error ( "GetUserRepoPermission(): %v" , err )
2018-11-28 04:26:14 -07:00
return false
2017-05-18 08:54:24 -06:00
}
2018-11-28 04:26:14 -07:00
return perm . CanRead ( unitType )
2017-02-04 08:53:46 -07:00
}
2016-11-28 10:27:55 -07:00
// CreateRepoOptions contains the create repository options
2015-08-28 04:33:09 -06:00
type CreateRepoOptions struct {
2020-01-10 08:35:17 -07:00
Name string
Description string
OriginalURL string
2020-01-20 13:01:19 -07:00
GitServiceType api . GitServiceType
2020-01-10 08:35:17 -07:00
Gitignores string
IssueLabels string
License string
Readme string
2020-03-26 13:14:51 -06:00
DefaultBranch string
2020-01-10 08:35:17 -07:00
IsPrivate bool
IsMirror bool
2020-09-24 23:18:37 -06:00
IsTemplate bool
2020-01-10 08:35:17 -07:00
AutoInit bool
2021-12-09 18:27:50 -07:00
Status repo_model . RepositoryStatus
TrustModel repo_model . TrustModelType
2021-01-02 16:47:47 -07:00
MirrorInterval string
2015-08-28 04:33:09 -06:00
}
2020-01-12 05:11:17 -07:00
// CreateRepository creates a repository for the user/organization.
2021-12-09 18:27:50 -07:00
func CreateRepository ( ctx context . Context , doer , u * user_model . User , repo * repo_model . Repository , overwriteOrAdopt bool ) ( err error ) {
2021-12-12 08:48:20 -07:00
if err = repo_model . IsUsableRepoName ( repo . Name ) ; err != nil {
2015-08-08 03:10:34 -06:00
return err
2014-06-18 23:08:03 -06:00
}
2022-05-20 08:08:52 -06:00
has , err := repo_model . IsRepositoryExist ( ctx , u , repo . Name )
2015-03-26 15:11:47 -06:00
if err != nil {
2015-08-08 03:10:34 -06:00
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
2015-03-26 15:11:47 -06:00
} else if has {
2021-12-12 08:48:20 -07:00
return repo_model . ErrRepoAlreadyExist {
Uname : u . Name ,
Name : repo . Name ,
}
2014-06-18 23:08:03 -06:00
}
2021-12-09 18:27:50 -07:00
repoPath := repo_model . RepoPath ( u . Name , repo . Name )
2020-11-27 19:42:08 -07:00
isExist , err := util . IsExist ( repoPath )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , repoPath , err )
return err
}
if ! overwriteOrAdopt && isExist {
2020-09-24 22:09:23 -06:00
log . Error ( "Files already exist in %s and we are not going to adopt or delete." , repoPath )
2021-12-12 08:48:20 -07:00
return repo_model . ErrRepoFilesAlreadyExist {
2020-09-24 22:09:23 -06:00
Uname : u . Name ,
Name : repo . Name ,
}
}
2021-12-09 18:27:50 -07:00
if err = db . Insert ( ctx , repo ) ; err != nil {
2015-08-08 03:10:34 -06:00
return err
2015-08-29 11:13:24 -06:00
}
2021-12-12 08:48:20 -07:00
if err = repo_model . DeleteRedirect ( ctx , u . ID , repo . Name ) ; err != nil {
2017-02-05 07:35:03 -07:00
return err
}
2015-08-29 11:13:24 -06:00
2017-02-04 08:53:46 -07:00
// insert units for repo
2021-12-09 18:27:50 -07:00
units := make ( [ ] repo_model . RepoUnit , 0 , len ( unit . DefaultRepoUnits ) )
2021-11-09 12:57:58 -07:00
for _ , tp := range unit . DefaultRepoUnits {
if tp == unit . TypeIssues {
2021-12-09 18:27:50 -07:00
units = append ( units , repo_model . RepoUnit {
2017-09-12 00:48:13 -06:00
RepoID : repo . ID ,
Type : tp ,
2021-12-09 18:27:50 -07:00
Config : & repo_model . IssuesConfig {
2018-07-17 15:23:58 -06:00
EnableTimetracker : setting . Service . DefaultEnableTimetracking ,
AllowOnlyContributorsToTrackTime : setting . Service . DefaultAllowOnlyContributorsToTrackTime ,
EnableDependencies : setting . Service . DefaultEnableDependencies ,
} ,
2017-09-12 00:48:13 -06:00
} )
2021-11-09 12:57:58 -07:00
} else if tp == unit . TypePullRequests {
2021-12-09 18:27:50 -07:00
units = append ( units , repo_model . RepoUnit {
2018-07-04 21:02:54 -06:00
RepoID : repo . ID ,
Type : tp ,
2022-06-02 21:45:54 -06:00
Config : & repo_model . PullRequestsConfig { AllowMerge : true , AllowRebase : true , AllowRebaseMerge : true , AllowSquash : true , DefaultMergeStyle : repo_model . MergeStyle ( setting . Repository . PullRequest . DefaultMergeStyle ) , AllowRebaseUpdate : true } ,
2018-07-04 21:02:54 -06:00
} )
2017-09-12 00:48:13 -06:00
} else {
2021-12-09 18:27:50 -07:00
units = append ( units , repo_model . RepoUnit {
2017-09-12 00:48:13 -06:00
RepoID : repo . ID ,
Type : tp ,
} )
}
2017-02-04 08:53:46 -07:00
}
2021-12-09 18:27:50 -07:00
if err = db . Insert ( ctx , units ) ; err != nil {
2017-02-04 08:53:46 -07:00
return err
}
2015-08-29 11:13:24 -06:00
// Remember visibility preference.
u . LastRepoVisibility = repo . IsPrivate
2022-05-20 08:08:52 -06:00
if err = user_model . UpdateUserCols ( ctx , u , "last_repo_visibility" ) ; err != nil {
2015-08-29 11:13:24 -06:00
return fmt . Errorf ( "updateUser: %v" , err )
2015-08-08 03:10:34 -06:00
}
2021-11-24 02:49:20 -07:00
if _ , err = db . GetEngine ( ctx ) . Incr ( "num_repos" ) . ID ( u . ID ) . Update ( new ( user_model . User ) ) ; err != nil {
2019-07-17 11:34:13 -06:00
return fmt . Errorf ( "increment user total_repos: %v" , err )
}
u . NumRepos ++
2019-11-06 02:37:14 -07:00
// Give access to all members in teams with access to all repositories.
2015-08-08 03:10:34 -06:00
if u . IsOrganization ( ) {
2022-03-29 00:29:02 -06:00
teams , err := organization . FindOrgTeams ( ctx , u . ID )
2021-11-19 04:41:40 -07:00
if err != nil {
2021-08-12 06:43:08 -06:00
return fmt . Errorf ( "loadTeams: %v" , err )
2019-10-26 00:54:11 -06:00
}
2021-11-19 04:41:40 -07:00
for _ , t := range teams {
2019-11-06 02:37:14 -07:00
if t . IncludesAllRepositories {
2022-03-29 00:29:02 -06:00
if err := addRepository ( ctx , t , repo ) ; err != nil {
2019-11-06 02:37:14 -07:00
return fmt . Errorf ( "addRepository: %v" , err )
}
}
2015-08-08 03:10:34 -06:00
}
2019-11-20 04:27:49 -07:00
2022-05-20 08:08:52 -06:00
if isAdmin , err := access_model . IsUserRepoAdmin ( ctx , repo , doer ) ; err != nil {
2022-05-03 13:46:28 -06:00
return fmt . Errorf ( "IsUserRepoAdminCtx: %v" , err )
2019-11-20 04:27:49 -07:00
} else if ! isAdmin {
2022-01-10 02:32:37 -07:00
// Make creator repo admin if it wasn't assigned automatically
2021-12-09 18:27:50 -07:00
if err = addCollaborator ( ctx , repo , doer ) ; err != nil {
2019-11-20 04:27:49 -07:00
return fmt . Errorf ( "AddCollaborator: %v" , err )
}
2022-05-11 04:09:36 -06:00
if err = repo_model . ChangeCollaborationAccessModeCtx ( ctx , repo , doer . ID , perm . AccessModeAdmin ) ; err != nil {
2019-11-20 04:27:49 -07:00
return fmt . Errorf ( "ChangeCollaborationAccessMode: %v" , err )
}
}
2022-05-11 04:09:36 -06:00
} else if err = access_model . RecalculateAccesses ( ctx , repo ) ; err != nil {
2015-08-08 03:10:34 -06:00
// Organization automatically called this in addRepository method.
2019-06-12 13:41:28 -06:00
return fmt . Errorf ( "recalculateAccesses: %v" , err )
2015-08-08 03:10:34 -06:00
}
2019-01-27 02:25:21 -07:00
if setting . Service . AutoWatchNewRepos {
2022-05-20 08:08:52 -06:00
if err = repo_model . WatchRepo ( ctx , doer . ID , repo . ID , true ) ; err != nil {
2019-01-27 02:25:21 -07:00
return fmt . Errorf ( "watchRepo: %v" , err )
}
}
2015-08-08 03:10:34 -06:00
2021-11-09 22:13:16 -07:00
if err = webhook . CopyDefaultWebhooksToRepo ( ctx , repo . ID ) ; err != nil {
2019-03-18 20:33:20 -06:00
return fmt . Errorf ( "copyDefaultWebhooksToRepo: %v" , err )
}
2015-08-08 03:10:34 -06:00
return nil
}
2014-12-06 18:22:48 -07:00
// DeleteRepository deletes a repository for a user or organization.
2021-01-18 13:00:50 -07:00
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
2021-11-24 02:49:20 -07:00
func DeleteRepository ( doer * user_model . User , uid , repoID int64 ) error {
2021-11-21 08:41:00 -07:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2021-01-18 13:00:50 -07:00
return err
}
2021-11-21 08:41:00 -07:00
defer committer . Close ( )
sess := db . GetEngine ( ctx )
2021-01-18 13:00:50 -07:00
2014-08-27 02:39:36 -06:00
// In case is a organization.
2022-05-20 08:08:52 -06:00
org , err := user_model . GetUserByIDCtx ( ctx , uid )
2014-08-27 02:39:36 -06:00
if err != nil {
return err
}
2021-12-09 18:27:50 -07:00
repo := & repo_model . Repository { OwnerID : uid }
2021-01-18 13:00:50 -07:00
has , err := sess . ID ( repoID ) . Get ( repo )
2017-02-28 21:05:45 -07:00
if err != nil {
return err
} else if ! has {
2021-12-09 18:27:50 -07:00
return repo_model . ErrRepoNotExist {
ID : repoID ,
UID : uid ,
OwnerName : "" ,
Name : "" ,
}
2017-02-28 21:05:45 -07:00
}
2019-02-03 16:56:53 -07:00
// Delete Deploy Keys
2021-12-10 01:14:24 -07:00
deployKeys , err := asymkey_model . ListDeployKeys ( ctx , & asymkey_model . ListDeployKeysOptions { RepoID : repoID } )
2019-02-03 16:56:53 -07:00
if err != nil {
return fmt . Errorf ( "listDeployKeys: %v" , err )
}
2022-01-20 10:46:10 -07:00
needRewriteKeysFile := len ( deployKeys ) > 0
2019-02-03 16:56:53 -07:00
for _ , dKey := range deployKeys {
2021-12-10 01:14:24 -07:00
if err := DeleteDeployKey ( ctx , doer , dKey . ID ) ; err != nil {
2019-02-03 16:56:53 -07:00
return fmt . Errorf ( "deleteDeployKeys: %v" , err )
}
}
2021-12-09 18:27:50 -07:00
if cnt , err := sess . ID ( repoID ) . Delete ( & repo_model . Repository { } ) ; err != nil {
2017-02-28 21:05:45 -07:00
return err
} else if cnt != 1 {
2021-12-09 18:27:50 -07:00
return repo_model . ErrRepoNotExist {
ID : repoID ,
UID : uid ,
OwnerName : "" ,
Name : "" ,
}
2017-02-28 21:05:45 -07:00
}
2014-08-27 02:39:36 -06:00
if org . IsOrganization ( ) {
2022-03-29 00:29:02 -06:00
teams , err := organization . FindOrgTeams ( ctx , org . ID )
2021-11-19 04:41:40 -07:00
if err != nil {
return err
}
for _ , t := range teams {
2022-05-11 04:09:36 -06:00
if ! organization . HasTeamRepo ( ctx , t . OrgID , t . ID , repoID ) {
2014-08-27 02:39:36 -06:00
continue
2022-03-29 00:29:02 -06:00
} else if err = removeRepository ( ctx , t , repo , false ) ; err != nil {
2014-08-27 02:39:36 -06:00
return err
}
}
}
2021-11-19 06:39:57 -07:00
attachments := make ( [ ] * repo_model . Attachment , 0 , 20 )
2019-12-11 22:31:05 -07:00
if err = sess . Join ( "INNER" , "`release`" , "`release`.id = `attachment`.release_id" ) .
Where ( "`release`.repo_id = ?" , repoID ) .
Find ( & attachments ) ; err != nil {
return err
}
releaseAttachments := make ( [ ] string , 0 , len ( attachments ) )
for i := 0 ; i < len ( attachments ) ; i ++ {
2020-08-17 22:23:45 -06:00
releaseAttachments = append ( releaseAttachments , attachments [ i ] . RelativePath ( ) )
2019-12-11 22:31:05 -07:00
}
2022-05-20 08:08:52 -06:00
if _ , err := db . Exec ( ctx , "UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)" , repo . ID ) ; err != nil {
2020-07-07 13:16:34 -06:00
return err
}
2022-02-17 01:37:48 -07:00
if err := db . DeleteBeans ( ctx ,
2022-05-11 04:09:36 -06:00
& access_model . Access { RepoID : repo . ID } ,
2015-11-30 18:45:55 -07:00
& Action { RepoID : repo . ID } ,
2022-05-11 04:09:36 -06:00
& repo_model . Collaboration { RepoID : repoID } ,
2021-04-30 13:10:39 -06:00
& Comment { RefRepoID : repoID } ,
& CommitStatus { RepoID : repoID } ,
& DeletedBranch { RepoID : repoID } ,
2021-11-09 22:13:16 -07:00
& webhook . HookTask { RepoID : repoID } ,
2021-04-30 13:10:39 -06:00
& LFSLock { RepoID : repoID } ,
2021-12-09 18:27:50 -07:00
& repo_model . LanguageStat { RepoID : repoID } ,
2022-04-08 03:11:15 -06:00
& issues_model . Milestone { RepoID : repoID } ,
2021-12-09 18:27:50 -07:00
& repo_model . Mirror { RepoID : repoID } ,
2018-12-10 13:01:01 -07:00
& Notification { RepoID : repoID } ,
2021-04-30 13:10:39 -06:00
& ProtectedBranch { RepoID : repoID } ,
2021-06-25 08:28:55 -06:00
& ProtectedTag { RepoID : repoID } ,
2021-12-09 18:27:50 -07:00
& repo_model . PushMirror { RepoID : repoID } ,
2021-04-30 13:10:39 -06:00
& Release { RepoID : repoID } ,
2021-12-09 18:27:50 -07:00
& repo_model . RepoIndexerStatus { RepoID : repoID } ,
2021-12-12 08:48:20 -07:00
& repo_model . Redirect { RedirectRepoID : repoID } ,
2021-12-09 18:27:50 -07:00
& repo_model . RepoUnit { RepoID : repoID } ,
2021-12-12 08:48:20 -07:00
& repo_model . Star { RepoID : repoID } ,
2019-10-13 07:23:14 -06:00
& Task { RepoID : repoID } ,
2021-12-12 08:48:20 -07:00
& repo_model . Watch { RepoID : repoID } ,
2021-11-09 22:13:16 -07:00
& webhook . Webhook { RepoID : repoID } ,
2015-11-30 18:45:55 -07:00
) ; err != nil {
return fmt . Errorf ( "deleteBeans: %v" , err )
2014-05-14 11:04:57 -06:00
}
2021-03-19 13:01:24 -06:00
// Delete Labels and related objects
2022-05-20 08:08:52 -06:00
if err := deleteLabelsByRepoID ( ctx , repoID ) ; err != nil {
2021-03-19 13:01:24 -06:00
return err
}
2022-05-08 07:46:34 -06:00
// Delete Pulls and related objects
2022-05-20 08:08:52 -06:00
if err := deletePullsByBaseRepoID ( ctx , repoID ) ; err != nil {
2022-05-08 07:46:34 -06:00
return err
}
2020-05-29 07:24:15 -06:00
// Delete Issues and related objects
var attachmentPaths [ ] string
2022-05-20 08:08:52 -06:00
if attachmentPaths , err = deleteIssuesByRepoID ( ctx , repoID ) ; err != nil {
2018-11-30 05:59:12 -07:00
return err
2014-05-14 07:23:33 -06:00
}
2014-10-18 23:35:24 -06:00
2021-06-13 20:22:55 -06:00
// Delete issue index
2022-05-20 08:08:52 -06:00
if err := db . DeleteResouceIndex ( ctx , "issue_index" , repoID ) ; err != nil {
2021-06-13 20:22:55 -06:00
return err
}
2014-10-13 13:23:30 -06:00
if repo . IsFork {
2022-05-20 08:08:52 -06:00
if _ , err := db . Exec ( ctx , "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repo . ForkID ) ; err != nil {
2015-09-01 09:43:53 -06:00
return fmt . Errorf ( "decrease fork count: %v" , err )
2014-10-18 23:35:24 -06:00
}
2014-10-13 13:23:30 -06:00
}
2014-04-12 18:35:35 -06:00
2022-05-20 08:08:52 -06:00
if _ , err := db . Exec ( ctx , "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?" , uid ) ; err != nil {
2014-04-12 18:35:35 -06:00
return err
}
2014-10-08 16:29:18 -06:00
2020-01-30 23:57:19 -07:00
if len ( repo . Topics ) > 0 {
2021-12-12 08:48:20 -07:00
if err := repo_model . RemoveTopicsFromRepo ( ctx , repo . ID ) ; err != nil {
2020-01-30 23:57:19 -07:00
return err
}
}
2022-05-20 08:08:52 -06:00
projects , _ , err := project_model . GetProjects ( ctx , project_model . SearchOptions {
2020-08-16 21:07:38 -06:00
RepoID : repoID ,
} )
if err != nil {
return fmt . Errorf ( "get projects: %v" , err )
}
for i := range projects {
2022-03-29 08:16:31 -06:00
if err := project_model . DeleteProjectByIDCtx ( ctx , projects [ i ] . ID ) ; err != nil {
2020-08-16 21:07:38 -06:00
return fmt . Errorf ( "delete project [%d]: %v" , projects [ i ] . ID , err )
}
}
2016-12-25 18:16:37 -07:00
// Remove LFS objects
var lfsObjects [ ] * LFSMetaObject
if err = sess . Where ( "repository_id=?" , repoID ) . Find ( & lfsObjects ) ; err != nil {
return err
}
2022-01-20 10:46:10 -07:00
lfsPaths := make ( [ ] string , 0 , len ( lfsObjects ) )
2016-12-25 18:16:37 -07:00
for _ , v := range lfsObjects {
2022-05-20 08:08:52 -06:00
count , err := db . CountByBean ( ctx , & LFSMetaObject { Pointer : lfs . Pointer { Oid : v . Oid } } )
2016-12-25 18:16:37 -07:00
if err != nil {
return err
}
if count > 1 {
continue
}
2021-09-08 09:19:30 -06:00
lfsPaths = append ( lfsPaths , v . RelativePath ( ) )
2016-12-25 18:16:37 -07:00
}
2022-05-20 08:08:52 -06:00
if _ , err := db . DeleteByBean ( ctx , & LFSMetaObject { RepositoryID : repoID } ) ; err != nil {
2016-12-25 18:16:37 -07:00
return err
}
2021-06-23 15:12:38 -06:00
// Remove archives
2021-12-06 00:19:28 -07:00
var archives [ ] * repo_model . RepoArchiver
2021-06-23 15:12:38 -06:00
if err = sess . Where ( "repo_id=?" , repoID ) . Find ( & archives ) ; err != nil {
return err
}
2022-01-20 10:46:10 -07:00
archivePaths := make ( [ ] string , 0 , len ( archives ) )
2021-06-23 15:12:38 -06:00
for _ , v := range archives {
p , _ := v . RelativePath ( )
2021-09-08 09:19:30 -06:00
archivePaths = append ( archivePaths , p )
2021-06-23 15:12:38 -06:00
}
2022-05-20 08:08:52 -06:00
if _ , err := db . DeleteByBean ( ctx , & repo_model . RepoArchiver { RepoID : repoID } ) ; err != nil {
2021-06-23 15:12:38 -06:00
return err
}
2015-09-01 09:43:53 -06:00
if repo . NumForks > 0 {
2017-01-27 09:11:41 -07:00
if _ , err = sess . Exec ( "UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?" , false , repo . ID ) ; err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "reset 'fork_id' and 'is_fork': %v" , err )
2016-07-08 23:42:05 -06:00
}
2015-09-01 09:43:53 -06:00
}
2021-09-08 09:19:30 -06:00
// Get all attachments with both issue_id and release_id are zero
2021-11-19 06:39:57 -07:00
var newAttachments [ ] * repo_model . Attachment
2021-09-08 09:19:30 -06:00
if err := sess . Where ( builder . Eq {
"repo_id" : repo . ID ,
"issue_id" : 0 ,
"release_id" : 0 ,
} ) . Find ( & newAttachments ) ; err != nil {
return err
}
2022-01-20 10:46:10 -07:00
newAttachmentPaths := make ( [ ] string , 0 , len ( newAttachments ) )
2021-09-08 09:19:30 -06:00
for _ , attach := range newAttachments {
newAttachmentPaths = append ( newAttachmentPaths , attach . RelativePath ( ) )
}
2021-11-19 06:39:57 -07:00
if _ , err := sess . Where ( "repo_id=?" , repo . ID ) . Delete ( new ( repo_model . Attachment ) ) ; err != nil {
2021-09-08 09:19:30 -06:00
return err
}
2021-11-21 08:41:00 -07:00
if err = committer . Commit ( ) ; err != nil {
2021-01-18 13:00:50 -07:00
return err
2017-01-27 09:11:41 -07:00
}
2021-11-21 08:41:00 -07:00
committer . Close ( )
2019-12-11 22:31:05 -07:00
2021-12-10 01:14:24 -07:00
if needRewriteKeysFile {
if err := asymkey_model . RewriteAllPublicKeys ( ) ; err != nil {
log . Error ( "RewriteAllPublicKeys failed: %v" , err )
}
}
2019-12-11 22:31:05 -07:00
// We should always delete the files after the database transaction succeed. If
2021-07-08 05:38:13 -06:00
// we delete the file but the database rollback, the repository will be broken.
2019-12-11 22:31:05 -07:00
2021-09-08 09:19:30 -06:00
// Remove repository files.
repoPath := repo . RepoPath ( )
2021-11-18 10:42:27 -07:00
admin_model . RemoveAllWithNotice ( db . DefaultContext , "Delete repository files" , repoPath )
2021-09-08 09:19:30 -06:00
// Remove wiki files
if repo . HasWiki ( ) {
2021-11-18 10:42:27 -07:00
admin_model . RemoveAllWithNotice ( db . DefaultContext , "Delete repository wiki" , repo . WikiPath ( ) )
2021-09-08 09:19:30 -06:00
}
// Remove archives
2022-03-03 04:51:13 -07:00
for _ , archive := range archivePaths {
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . RepoArchives , "Delete repo archive file" , archive )
2021-09-08 09:19:30 -06:00
}
// Remove lfs objects
2022-03-03 04:51:13 -07:00
for _ , lfsObj := range lfsPaths {
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . LFS , "Delete orphaned LFS file" , lfsObj )
2021-09-08 09:19:30 -06:00
}
2019-12-11 22:31:05 -07:00
// Remove issue attachment files.
2022-03-03 04:51:13 -07:00
for _ , attachment := range attachmentPaths {
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete issue attachment" , attachment )
2019-12-11 22:31:05 -07:00
}
// Remove release attachment files.
2022-03-03 04:51:13 -07:00
for _ , releaseAttachment := range releaseAttachments {
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete release attachment" , releaseAttachment )
2019-12-11 22:31:05 -07:00
}
2021-09-08 09:19:30 -06:00
// Remove attachment with no issue_id and release_id.
2022-03-03 04:51:13 -07:00
for _ , newAttachment := range newAttachmentPaths {
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete issue attachment" , newAttachment )
2021-09-08 09:19:30 -06:00
}
2019-05-29 20:22:26 -06:00
if len ( repo . Avatar ) > 0 {
2020-10-14 07:07:51 -06:00
if err := storage . RepoAvatars . Delete ( repo . CustomAvatarRelativePath ( ) ) ; err != nil {
return fmt . Errorf ( "Failed to remove %s: %v" , repo . Avatar , err )
2019-05-29 20:22:26 -06:00
}
}
2015-09-01 09:43:53 -06:00
return nil
2014-03-20 14:04:56 -06:00
}
2015-09-01 09:43:53 -06:00
type repoChecker struct {
2022-01-17 11:31:58 -07:00
querySQL func ( ctx context . Context ) ( [ ] map [ string ] [ ] byte , error )
correctSQL func ( ctx context . Context , id int64 ) error
desc string
2015-09-01 09:43:53 -06:00
}
2015-08-17 14:03:11 -06:00
2019-12-15 02:51:28 -07:00
func repoStatsCheck ( ctx context . Context , checker * repoChecker ) {
2022-01-17 11:31:58 -07:00
results , err := checker . querySQL ( ctx )
2015-08-17 14:03:11 -06:00
if err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "Select %s: %v" , checker . desc , err )
2015-08-17 14:03:11 -06:00
return
}
2015-09-01 09:43:53 -06:00
for _ , result := range results {
2020-12-25 02:59:32 -07:00
id , _ := strconv . ParseInt ( string ( result [ "id" ] ) , 10 , 64 )
2019-12-15 02:51:28 -07:00
select {
case <- ctx . Done ( ) :
2022-01-17 11:31:58 -07:00
log . Warn ( "CheckRepoStats: Cancelled before checking %s for with id=%d" , checker . desc , id )
2019-12-15 02:51:28 -07:00
return
default :
}
2015-09-01 09:43:53 -06:00
log . Trace ( "Updating %s: %d" , checker . desc , id )
2022-01-17 11:31:58 -07:00
err = checker . correctSQL ( ctx , id )
2015-08-17 14:03:11 -06:00
if err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "Update %s[%d]: %v" , checker . desc , id , err )
2015-08-17 14:03:11 -06:00
}
}
2015-09-01 09:43:53 -06:00
}
2015-08-17 14:03:11 -06:00
2022-01-17 11:31:58 -07:00
func StatsCorrectSQL ( ctx context . Context , sql string , id int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( sql , id , id )
return err
}
func repoStatsCorrectNumWatches ( ctx context . Context , id int64 ) error {
return StatsCorrectSQL ( ctx , "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=? AND mode<>2) WHERE id=?" , id )
}
func repoStatsCorrectNumStars ( ctx context . Context , id int64 ) error {
return StatsCorrectSQL ( ctx , "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?" , id )
}
func labelStatsCorrectNumIssues ( ctx context . Context , id int64 ) error {
return StatsCorrectSQL ( ctx , "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?" , id )
}
func labelStatsCorrectNumIssuesRepo ( ctx context . Context , id int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=id) WHERE repo_id=?" , id )
return err
}
func labelStatsCorrectNumClosedIssues ( ctx context . Context , id int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `label` SET num_closed_issues=(SELECT COUNT(*) FROM `issue_label`,`issue` WHERE `issue_label`.label_id=`label`.id AND `issue_label`.issue_id=`issue`.id AND `issue`.is_closed=?) WHERE `label`.id=?" , true , id )
return err
}
func labelStatsCorrectNumClosedIssuesRepo ( ctx context . Context , id int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `label` SET num_closed_issues=(SELECT COUNT(*) FROM `issue_label`,`issue` WHERE `issue_label`.label_id=`label`.id AND `issue_label`.issue_id=`issue`.id AND `issue`.is_closed=?) WHERE `label`.repo_id=?" , true , id )
return err
}
var milestoneStatsQueryNumIssues = "SELECT `milestone`.id FROM `milestone` WHERE `milestone`.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE `issue`.milestone_id=`milestone`.id AND `issue`.is_closed=?) OR `milestone`.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE `issue`.milestone_id=`milestone`.id)"
func milestoneStatsCorrectNumIssuesRepo ( ctx context . Context , id int64 ) error {
e := db . GetEngine ( ctx )
results , err := e . Query ( milestoneStatsQueryNumIssues + " AND `milestone`.repo_id = ?" , true , id )
if err != nil {
return err
}
for _ , result := range results {
id , _ := strconv . ParseInt ( string ( result [ "id" ] ) , 10 , 64 )
2022-04-08 03:11:15 -06:00
err = issues_model . UpdateMilestoneCounters ( ctx , id )
2022-01-17 11:31:58 -07:00
if err != nil {
return err
}
}
return nil
}
func userStatsCorrectNumRepos ( ctx context . Context , id int64 ) error {
return StatsCorrectSQL ( ctx , "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?" , id )
}
func repoStatsCorrectIssueNumComments ( ctx context . Context , id int64 ) error {
return StatsCorrectSQL ( ctx , "UPDATE `issue` SET num_comments=(SELECT COUNT(*) FROM `comment` WHERE issue_id=? AND type=0) WHERE id=?" , id )
}
func repoStatsCorrectNumIssues ( ctx context . Context , id int64 ) error {
return repoStatsCorrectNum ( ctx , id , false , "num_issues" )
}
func repoStatsCorrectNumPulls ( ctx context . Context , id int64 ) error {
return repoStatsCorrectNum ( ctx , id , true , "num_pulls" )
}
func repoStatsCorrectNum ( ctx context . Context , id int64 , isPull bool , field string ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `repository` SET " + field + "=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=?) WHERE id=?" , id , isPull , id )
return err
}
func repoStatsCorrectNumClosedIssues ( ctx context . Context , id int64 ) error {
return repoStatsCorrectNumClosed ( ctx , id , false , "num_closed_issues" )
}
func repoStatsCorrectNumClosedPulls ( ctx context . Context , id int64 ) error {
return repoStatsCorrectNumClosed ( ctx , id , true , "num_closed_pulls" )
}
func repoStatsCorrectNumClosed ( ctx context . Context , id int64 , isPull bool , field string ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `repository` SET " + field + "=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?" , id , true , isPull , id )
return err
}
func statsQuery ( args ... interface { } ) func ( context . Context ) ( [ ] map [ string ] [ ] byte , error ) {
return func ( ctx context . Context ) ( [ ] map [ string ] [ ] byte , error ) {
return db . GetEngine ( ctx ) . Query ( args ... )
}
}
2016-11-28 10:27:55 -07:00
// CheckRepoStats checks the repository stats
2020-05-16 17:31:38 -06:00
func CheckRepoStats ( ctx context . Context ) error {
2015-09-01 09:43:53 -06:00
log . Trace ( "Doing: CheckRepoStats" )
2015-08-29 11:13:24 -06:00
2015-09-01 09:43:53 -06:00
checkers := [ ] * repoChecker {
// Repository.NumWatches
{
2022-01-17 11:31:58 -07:00
statsQuery ( "SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id AND mode<>2)" ) ,
repoStatsCorrectNumWatches ,
2015-09-01 09:43:53 -06:00
"repository count 'num_watches'" ,
} ,
// Repository.NumStars
{
2022-01-17 11:31:58 -07:00
statsQuery ( "SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)" ) ,
repoStatsCorrectNumStars ,
2015-09-01 09:43:53 -06:00
"repository count 'num_stars'" ,
} ,
2022-01-17 11:31:58 -07:00
// Repository.NumClosedIssues
{
statsQuery ( "SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)" , true , false ) ,
repoStatsCorrectNumClosedIssues ,
"repository count 'num_closed_issues'" ,
} ,
// Repository.NumClosedPulls
{
statsQuery ( "SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)" , true , true ) ,
repoStatsCorrectNumClosedPulls ,
"repository count 'num_closed_pulls'" ,
} ,
2015-09-01 09:43:53 -06:00
// Label.NumIssues
{
2022-01-17 11:31:58 -07:00
statsQuery ( "SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)" ) ,
labelStatsCorrectNumIssues ,
2015-09-01 09:43:53 -06:00
"label count 'num_issues'" ,
} ,
2022-01-17 11:31:58 -07:00
// Label.NumClosedIssues
{
statsQuery ( "SELECT `label`.id FROM `label` WHERE `label`.num_closed_issues!=(SELECT COUNT(*) FROM `issue_label`,`issue` WHERE `issue_label`.label_id=`label`.id AND `issue_label`.issue_id=`issue`.id AND `issue`.is_closed=?)" , true ) ,
labelStatsCorrectNumClosedIssues ,
"label count 'num_closed_issues'" ,
} ,
// Milestone.Num{,Closed}Issues
{
statsQuery ( milestoneStatsQueryNumIssues , true ) ,
2022-04-08 03:11:15 -06:00
issues_model . UpdateMilestoneCounters ,
2022-01-17 11:31:58 -07:00
"milestone count 'num_closed_issues' and 'num_issues'" ,
} ,
2015-09-01 09:43:53 -06:00
// User.NumRepos
{
2022-01-17 11:31:58 -07:00
statsQuery ( "SELECT `user`.id FROM `user` WHERE `user`.num_repos!=(SELECT COUNT(*) FROM `repository` WHERE owner_id=`user`.id)" ) ,
userStatsCorrectNumRepos ,
2015-09-01 09:43:53 -06:00
"user count 'num_repos'" ,
} ,
2015-10-29 18:40:57 -06:00
// Issue.NumComments
{
2022-01-17 11:31:58 -07:00
statsQuery ( "SELECT `issue`.id FROM `issue` WHERE `issue`.num_comments!=(SELECT COUNT(*) FROM `comment` WHERE issue_id=`issue`.id AND type=0)" ) ,
repoStatsCorrectIssueNumComments ,
2015-10-29 18:40:57 -06:00
"issue count 'num_comments'" ,
} ,
2015-09-01 09:43:53 -06:00
}
2020-05-16 17:31:38 -06:00
for _ , checker := range checkers {
2019-12-15 02:51:28 -07:00
select {
case <- ctx . Done ( ) :
2020-05-16 17:31:38 -06:00
log . Warn ( "CheckRepoStats: Cancelled before %s" , checker . desc )
2021-11-09 22:13:16 -07:00
return db . ErrCancelledf ( "before checking %s" , checker . desc )
2019-12-15 02:51:28 -07:00
default :
2020-05-16 17:31:38 -06:00
repoStatsCheck ( ctx , checker )
2019-12-15 02:51:28 -07:00
}
2015-09-01 09:43:53 -06:00
}
2016-05-27 19:23:39 -06:00
// FIXME: use checker when stop supporting old fork repo format.
2015-09-01 09:43:53 -06:00
// ***** START: Repository.NumForks *****
2022-01-17 11:31:58 -07:00
e := db . GetEngine ( ctx )
results , err := e . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_forks!=(SELECT COUNT(*) FROM `repository` WHERE fork_id=repo.id)" )
2015-08-29 11:13:24 -06:00
if err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "Select repository count 'num_forks': %v" , err )
2015-09-01 09:43:53 -06:00
} else {
for _ , result := range results {
2020-12-25 02:59:32 -07:00
id , _ := strconv . ParseInt ( string ( result [ "id" ] ) , 10 , 64 )
2019-12-15 02:51:28 -07:00
select {
case <- ctx . Done ( ) :
2020-05-16 17:31:38 -06:00
log . Warn ( "CheckRepoStats: Cancelled" )
2022-01-17 11:31:58 -07:00
return db . ErrCancelledf ( "during repository count 'num_fork' for repo ID %d" , id )
2019-12-15 02:51:28 -07:00
default :
}
2015-09-01 09:43:53 -06:00
log . Trace ( "Updating repository count 'num_forks': %d" , id )
2021-12-09 18:27:50 -07:00
repo , err := repo_model . GetRepositoryByID ( id )
2015-09-01 09:43:53 -06:00
if err != nil {
2021-12-09 18:27:50 -07:00
log . Error ( "repo_model.GetRepositoryByID[%d]: %v" , id , err )
2015-09-01 09:43:53 -06:00
continue
}
2022-06-06 02:01:49 -06:00
rawResult , err := e . Query ( "SELECT COUNT(*) FROM `repository` WHERE fork_id=?" , repo . ID )
2015-09-01 09:43:53 -06:00
if err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "Select count of forks[%d]: %v" , repo . ID , err )
2015-09-01 09:43:53 -06:00
continue
}
repo . NumForks = int ( parseCountResult ( rawResult ) )
2022-06-06 02:01:49 -06:00
if _ , err = e . ID ( repo . ID ) . Cols ( "num_forks" ) . Update ( repo ) ; err != nil {
2019-04-02 01:48:31 -06:00
log . Error ( "UpdateRepository[%d]: %v" , id , err )
2015-09-01 09:43:53 -06:00
continue
}
2015-08-29 11:13:24 -06:00
}
}
2015-09-01 09:43:53 -06:00
// ***** END: Repository.NumForks *****
2020-05-16 17:31:38 -06:00
return nil
2015-03-21 06:55:00 -06:00
}
2022-01-17 11:31:58 -07:00
func UpdateRepoStats ( ctx context . Context , id int64 ) error {
var err error
for _ , f := range [ ] func ( ctx context . Context , id int64 ) error {
repoStatsCorrectNumWatches ,
repoStatsCorrectNumStars ,
repoStatsCorrectNumIssues ,
repoStatsCorrectNumPulls ,
repoStatsCorrectNumClosedIssues ,
repoStatsCorrectNumClosedPulls ,
labelStatsCorrectNumIssuesRepo ,
labelStatsCorrectNumClosedIssuesRepo ,
milestoneStatsCorrectNumIssuesRepo ,
} {
err = f ( ctx , id )
if err != nil {
return err
}
}
return nil
}
2021-11-24 02:49:20 -07:00
func updateUserStarNumbers ( users [ ] user_model . User ) error {
2021-11-21 08:41:00 -07:00
ctx , committer , err := db . TxContext ( )
if err != nil {
return err
}
defer committer . Close ( )
for _ , user := range users {
if _ , err = db . Exec ( ctx , "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?" , user . ID , user . ID ) ; err != nil {
return err
}
}
return committer . Commit ( )
}
2020-07-07 13:16:34 -06:00
// DoctorUserStarNum recalculate Stars number for all user
func DoctorUserStarNum ( ) ( err error ) {
const batchSize = 100
for start := 0 ; ; start += batchSize {
2021-11-24 02:49:20 -07:00
users := make ( [ ] user_model . User , 0 , batchSize )
2021-11-21 08:41:00 -07:00
if err = db . GetEngine ( db . DefaultContext ) . Limit ( batchSize , start ) . Where ( "type = ?" , 0 ) . Cols ( "id" ) . Find ( & users ) ; err != nil {
2020-07-07 13:16:34 -06:00
return
}
if len ( users ) == 0 {
break
}
2021-11-21 08:41:00 -07:00
if err = updateUserStarNumbers ( users ) ; err != nil {
2020-07-07 13:16:34 -06:00
return
}
}
log . Debug ( "recalculate Stars number for all user finished" )
return
}
2020-10-14 07:07:51 -06:00
2021-12-10 01:14:24 -07:00
// DeleteDeployKey delete deploy keys
func DeleteDeployKey ( ctx context . Context , doer * user_model . User , id int64 ) error {
key , err := asymkey_model . GetDeployKeyByID ( ctx , id )
if err != nil {
if asymkey_model . IsErrDeployKeyNotExist ( err ) {
return nil
}
return fmt . Errorf ( "GetDeployKeyByID: %v" , err )
}
// Check if user has access to delete this key.
if ! doer . IsAdmin {
repo , err := repo_model . GetRepositoryByIDCtx ( ctx , key . RepoID )
if err != nil {
return fmt . Errorf ( "GetRepositoryByID: %v" , err )
}
2022-05-20 08:08:52 -06:00
has , err := access_model . IsUserRepoAdmin ( ctx , repo , doer )
2021-12-10 01:14:24 -07:00
if err != nil {
return fmt . Errorf ( "GetUserRepoPermission: %v" , err )
} else if ! has {
return asymkey_model . ErrKeyAccessDenied {
UserID : doer . ID ,
KeyID : key . ID ,
Note : "deploy" ,
}
}
}
2022-06-06 02:01:49 -06:00
if _ , err := db . DeleteByBean ( ctx , & asymkey_model . DeployKey {
ID : key . ID ,
} ) ; err != nil {
2021-12-10 01:14:24 -07:00
return fmt . Errorf ( "delete deploy key [%d]: %v" , key . ID , err )
}
// Check if this is the last reference to same key content.
2022-06-06 02:01:49 -06:00
has , err := asymkey_model . IsDeployKeyExistByKeyID ( ctx , key . KeyID )
2021-12-10 01:14:24 -07:00
if err != nil {
return err
} else if ! has {
if err = asymkey_model . DeletePublicKeys ( ctx , key . KeyID ) ; err != nil {
return err
}
}
return nil
}