2014-02-15 04:46:54 +05:30
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-08-26 19:27:41 +05:30
// Copyright 2017 The Gitea Authors. All rights reserved.
2014-02-15 04:46:54 +05:30
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-14 19:50:57 +05:30
package models
import (
2019-12-15 15:21:28 +05:30
"context"
2014-03-11 06:18:58 +05:30
"fmt"
2014-02-14 19:50:57 +05:30
"os"
2014-03-30 07:48:36 +05:30
"path"
2014-02-14 19:50:57 +05:30
"path/filepath"
2014-06-03 08:47:21 +05:30
"sort"
2019-04-23 02:10:51 +05:30
"strconv"
2014-02-14 19:50:57 +05:30
"strings"
2021-09-22 11:08:34 +05:30
"unicode/utf8"
2014-02-14 19:50:57 +05:30
2021-11-17 18:04:35 +05:30
_ "image/jpeg" // Needed for jpeg support
2021-11-18 11:28:42 +05:30
admin_model "code.gitea.io/gitea/models/admin"
2021-12-10 13:44:24 +05:30
asymkey_model "code.gitea.io/gitea/models/asymkey"
2021-09-19 17:19:59 +05:30
"code.gitea.io/gitea/models/db"
2021-11-28 17:28:28 +05:30
"code.gitea.io/gitea/models/perm"
2021-11-19 19:09:57 +05:30
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 01:27:58 +05:30
"code.gitea.io/gitea/models/unit"
2021-11-24 15:19:20 +05:30
user_model "code.gitea.io/gitea/models/user"
2021-11-10 10:43:16 +05:30
"code.gitea.io/gitea/models/webhook"
2021-04-09 03:55:57 +05:30
"code.gitea.io/gitea/modules/lfs"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/log"
2016-12-22 23:42:23 +05:30
"code.gitea.io/gitea/modules/options"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/setting"
2020-08-18 09:53:45 +05:30
"code.gitea.io/gitea/modules/storage"
2019-05-11 15:51:34 +05:30
api "code.gitea.io/gitea/modules/structs"
2019-11-11 03:03:47 +05:30
"code.gitea.io/gitea/modules/util"
2017-01-01 23:45:09 +05:30
2020-06-13 17:05:59 +05:30
"xorm.io/builder"
2014-02-14 19:50:57 +05:30
)
2014-03-10 05:36:29 +05:30
var (
2016-11-28 22:57:55 +05:30
// Gitignores contains the gitiginore files
Gitignores [ ] string
// Licenses contains the license files
Licenses [ ] string
// Readmes contains the readme files
Readmes [ ] string
2019-12-07 07:43:19 +05:30
// LabelTemplates contains the label template files and the list of labels for each file
LabelTemplates map [ string ] string
2015-10-01 18:47:27 +05:30
2016-11-28 22:57:55 +05:30
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
2015-11-17 09:58:46 +05:30
ItemsPerPage = 40
2014-03-10 05:36:29 +05:30
)
2019-05-15 07:27:00 +05:30
// loadRepoConfig loads the repository config
func loadRepoConfig ( ) {
2015-08-28 14:14:04 +05:30
// Load .gitignore and license files and readme templates.
2016-08-30 07:32:49 +05:30
types := [ ] string { "gitignore" , "license" , "readme" , "label" }
typeFiles := make ( [ ] [ ] string , 4 )
2014-05-11 23:33:51 +05:30
for i , t := range types {
2016-12-22 23:42:23 +05:30
files , err := options . Dir ( t )
2014-07-26 09:54:27 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Fatal ( "Failed to get %s files: %v" , t , err )
2014-07-26 09:54:27 +05:30
}
2016-12-22 23:42:23 +05:30
customPath := path . Join ( setting . CustomPath , "options" , t )
2020-11-28 08:12:08 +05:30
isDir , err := util . IsDir ( customPath )
if err != nil {
log . Fatal ( "Failed to get custom %s files: %v" , t , err )
}
if isDir {
2020-12-22 05:10:57 +05:30
customFiles , err := util . StatDir ( customPath )
2014-05-11 23:33:51 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Fatal ( "Failed to get custom %s files: %v" , t , err )
2014-05-11 23:33:51 +05:30
}
for _ , f := range customFiles {
2020-12-25 15:29:32 +05:30
if ! util . IsStringInSlice ( f , files , true ) {
2014-05-11 23:33:51 +05:30
files = append ( files , f )
}
}
}
typeFiles [ i ] = files
}
2014-07-26 09:54:27 +05:30
Gitignores = typeFiles [ 0 ]
2014-05-11 23:33:51 +05:30
Licenses = typeFiles [ 1 ]
2015-08-28 14:14:04 +05:30
Readmes = typeFiles [ 2 ]
2019-12-07 07:43:19 +05:30
LabelTemplatesFiles := typeFiles [ 3 ]
2014-07-26 09:54:27 +05:30
sort . Strings ( Gitignores )
2014-06-03 08:47:21 +05:30
sort . Strings ( Licenses )
2015-08-28 14:14:04 +05:30
sort . Strings ( Readmes )
2019-12-07 07:43:19 +05:30
sort . Strings ( LabelTemplatesFiles )
// Load label templates
LabelTemplates = make ( map [ string ] string )
for _ , templateFile := range LabelTemplatesFiles {
labels , err := LoadLabelsFormatted ( templateFile )
if err != nil {
log . Error ( "Failed to load labels: %v" , err )
}
LabelTemplates [ templateFile ] = labels
}
2016-08-28 12:36:22 +05:30
// Filter out invalid names and promote preferred licenses.
sortedLicenses := make ( [ ] string , 0 , len ( Licenses ) )
for _ , name := range setting . Repository . PreferredLicenses {
2020-12-25 15:29:32 +05:30
if util . IsStringInSlice ( name , Licenses , true ) {
2016-08-28 12:36:22 +05:30
sortedLicenses = append ( sortedLicenses , name )
}
}
for _ , name := range Licenses {
2020-12-25 15:29:32 +05:30
if ! util . IsStringInSlice ( name , setting . Repository . PreferredLicenses , true ) {
2016-08-28 12:36:22 +05:30
sortedLicenses = append ( sortedLicenses , name )
}
}
Licenses = sortedLicenses
2014-03-21 11:18:10 +05:30
}
2014-03-17 21:26:50 +05:30
2016-11-28 22:57:55 +05:30
// NewRepoContext creates a new repository context
2014-03-21 11:18:10 +05:30
func NewRepoContext ( ) {
2019-05-15 07:27:00 +05:30
loadRepoConfig ( )
2021-11-10 01:27:58 +05:30
unit . LoadUnitConfig ( )
2014-08-23 21:28:56 +05:30
2021-11-18 23:12:27 +05:30
admin_model . RemoveAllWithNotice ( db . DefaultContext , "Clean up repository temporary data" , filepath . Join ( setting . AppDataPath , "tmp" ) )
2014-03-11 11:02:36 +05:30
}
2021-12-10 06:57:50 +05:30
// CheckRepoUnitUser check whether user could visit the unit of this repository
func CheckRepoUnitUser ( repo * repo_model . Repository , user * user_model . User , unitType unit . Type ) bool {
return checkRepoUnitUser ( db . DefaultContext , repo , user , unitType )
2016-08-14 16:02:24 +05:30
}
2021-12-10 06:57:50 +05:30
func checkRepoUnitUser ( ctx context . Context , repo * repo_model . Repository , user * user_model . User , unitType unit . Type ) bool {
2020-09-16 05:19:34 +05:30
if user . IsAdmin {
2018-11-28 16:56:14 +05:30
return true
2017-06-15 08:20:12 +05:30
}
2021-12-10 06:57:50 +05:30
perm , err := getUserRepoPermission ( ctx , repo , user )
2018-11-28 16:56:14 +05:30
if err != nil {
2020-09-16 05:19:34 +05:30
log . Error ( "getUserRepoPermission(): %v" , err )
2018-11-28 16:56:14 +05:30
return false
2017-05-18 20:24:24 +05:30
}
2018-11-28 16:56:14 +05:30
return perm . CanRead ( unitType )
2017-02-04 21:23:46 +05:30
}
2021-12-10 06:57:50 +05:30
func getRepoAssignees ( ctx context . Context , repo * repo_model . Repository ) ( _ [ ] * user_model . User , err error ) {
if err = repo . GetOwner ( ctx ) ; err != nil {
2015-08-10 19:17:23 +05:30
return nil , err
}
2021-12-10 06:57:50 +05:30
e := db . GetEngine ( ctx )
2015-08-10 19:17:23 +05:30
accesses := make ( [ ] * Access , 0 , 10 )
2016-11-10 20:46:32 +05:30
if err = e .
2021-11-28 17:28:28 +05:30
Where ( "repo_id = ? AND mode >= ?" , repo . ID , perm . AccessModeWrite ) .
2016-11-10 20:46:32 +05:30
Find ( & accesses ) ; err != nil {
2015-08-10 19:17:23 +05:30
return nil , err
}
2016-08-16 07:10:32 +05:30
// Leave a seat for owner itself to append later, but if owner is an organization
// and just waste 1 unit is cheaper than re-allocate memory once.
2021-11-24 15:19:20 +05:30
users := make ( [ ] * user_model . User , 0 , len ( accesses ) + 1 )
2016-08-16 07:18:20 +05:30
if len ( accesses ) > 0 {
userIDs := make ( [ ] int64 , len ( accesses ) )
for i := 0 ; i < len ( accesses ) ; i ++ {
userIDs [ i ] = accesses [ i ] . UserID
}
if err = e . In ( "id" , userIDs ) . Find ( & users ) ; err != nil {
return nil , err
}
2016-08-16 07:10:32 +05:30
}
2015-08-10 19:17:23 +05:30
if ! repo . Owner . IsOrganization ( ) {
users = append ( users , repo . Owner )
}
return users , nil
}
2021-12-10 06:57:50 +05:30
// GetRepoAssignees returns all users that have write access and can be assigned to issues
2016-08-16 07:10:32 +05:30
// of the repository,
2021-12-10 06:57:50 +05:30
func GetRepoAssignees ( repo * repo_model . Repository ) ( _ [ ] * user_model . User , err error ) {
return getRepoAssignees ( db . DefaultContext , repo )
2016-08-16 07:10:32 +05:30
}
2021-12-10 06:57:50 +05:30
func getReviewers ( ctx context . Context , repo * repo_model . Repository , doerID , posterID int64 ) ( [ ] * user_model . User , error ) {
2020-10-13 01:25:13 +05:30
// Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
2021-12-10 06:57:50 +05:30
if err := repo . GetOwner ( ctx ) ; err != nil {
2020-04-06 22:03:34 +05:30
return nil , err
}
2021-11-24 15:19:20 +05:30
var users [ ] * user_model . User
2021-12-10 06:57:50 +05:30
e := db . GetEngine ( ctx )
2020-04-06 22:03:34 +05:30
2021-06-27 01:23:14 +05:30
if repo . IsPrivate || repo . Owner . Visibility == api . VisibleTypePrivate {
2020-10-13 01:25:13 +05:30
// This a private repository:
// Anyone who can read the repository is a requestable reviewer
if err := e .
SQL ( "SELECT * FROM `user` WHERE id in (SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? AND user_id NOT IN ( ?, ?)) ORDER BY name" ,
2021-11-28 17:28:28 +05:30
repo . ID , perm . AccessModeRead ,
2020-10-13 01:25:13 +05:30
doerID , posterID ) .
Find ( & users ) ; err != nil {
return nil , err
}
2020-04-06 22:03:34 +05:30
2020-10-13 01:25:13 +05:30
return users , nil
}
2020-04-06 22:03:34 +05:30
2020-10-13 01:25:13 +05:30
// This is a "public" repository:
2021-02-28 23:54:00 +05:30
// Any user that has read access, is a watcher or organization member can be requested to review
2020-10-13 01:25:13 +05:30
if err := e .
SQL ( "SELECT * FROM `user` WHERE id IN ( " +
2021-02-28 23:54:00 +05:30
"SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? " +
2020-10-13 01:25:13 +05:30
"UNION " +
2021-02-28 23:54:00 +05:30
"SELECT user_id FROM `watch` WHERE repo_id = ? AND mode IN (?, ?) " +
"UNION " +
"SELECT uid AS user_id FROM `org_user` WHERE org_id = ? " +
") AND id NOT IN (?, ?) ORDER BY name" ,
2021-11-28 17:28:28 +05:30
repo . ID , perm . AccessModeRead ,
2021-12-12 21:18:20 +05:30
repo . ID , repo_model . WatchModeNormal , repo_model . WatchModeAuto ,
2021-02-28 23:54:00 +05:30
repo . OwnerID ,
doerID , posterID ) .
2020-04-06 22:03:34 +05:30
Find ( & users ) ; err != nil {
return nil , err
}
return users , nil
}
2020-10-13 01:25:13 +05:30
// GetReviewers get all users can be requested to review:
// * for private repositories this returns all users that have read access or higher to the repository.
2021-02-28 23:54:00 +05:30
// * for public repositories this returns all users that have read access or higher to the repository,
// all repo watchers and all organization members.
2021-02-18 21:09:04 +05:30
// TODO: may be we should have a busy choice for users to block review request to them.
2021-12-10 06:57:50 +05:30
func GetReviewers ( repo * repo_model . Repository , doerID , posterID int64 ) ( [ ] * user_model . User , error ) {
return getReviewers ( db . DefaultContext , repo , doerID , posterID )
2020-10-13 01:25:13 +05:30
}
// GetReviewerTeams get all teams can be requested to review
2021-12-10 06:57:50 +05:30
func GetReviewerTeams ( repo * repo_model . Repository ) ( [ ] * Team , error ) {
if err := repo . GetOwner ( db . DefaultContext ) ; err != nil {
2020-04-06 22:03:34 +05:30
return nil , err
}
2020-10-13 01:25:13 +05:30
if ! repo . Owner . IsOrganization ( ) {
return nil , nil
}
2020-04-06 22:03:34 +05:30
2021-11-28 17:28:28 +05:30
teams , err := GetTeamsWithAccessToRepo ( repo . OwnerID , repo . ID , perm . AccessModeRead )
2020-10-13 01:25:13 +05:30
if err != nil {
return nil , err
2020-04-06 22:03:34 +05:30
}
2020-10-13 01:25:13 +05:30
return teams , err
2020-04-06 22:03:34 +05:30
}
2021-12-10 06:57:50 +05:30
func updateRepoSize ( e db . Engine , repo * repo_model . Repository ) error {
2020-01-12 15:06:21 +05:30
size , err := util . GetDirectorySize ( repo . RepoPath ( ) )
2017-04-11 19:00:15 +05:30
if err != nil {
2020-01-12 17:41:17 +05:30
return fmt . Errorf ( "updateSize: %v" , err )
2017-04-11 19:00:15 +05:30
}
2021-03-28 09:26:28 +05:30
lfsSize , err := e . Where ( "repository_id = ?" , repo . ID ) . SumInt ( new ( LFSMetaObject ) , "size" )
2020-05-31 10:21:19 +05:30
if err != nil {
return fmt . Errorf ( "updateSize: GetLFSMetaObjects: %v" , err )
}
2021-03-28 09:26:28 +05:30
repo . Size = size + lfsSize
2021-04-27 22:03:16 +05:30
_ , err = e . ID ( repo . ID ) . Cols ( "size" ) . NoAutoTime ( ) . Update ( repo )
2017-04-11 19:00:15 +05:30
return err
}
2021-12-10 06:57:50 +05:30
// UpdateRepoSize updates the repository size, calculating it using util.GetDirectorySize
func UpdateRepoSize ( ctx context . Context , repo * repo_model . Repository ) error {
return updateRepoSize ( db . GetEngine ( ctx ) , repo )
2017-05-26 10:38:13 +05:30
}
2021-11-22 20:51:55 +05:30
// CanUserForkRepo returns true if specified user can fork repository.
2021-12-10 06:57:50 +05:30
func CanUserForkRepo ( user * user_model . User , repo * repo_model . Repository ) ( bool , error ) {
2017-10-15 20:36:07 +05:30
if user == nil {
return false , nil
}
2021-12-12 21:18:20 +05:30
if repo . OwnerID != user . ID && ! repo_model . HasForkedRepo ( user . ID , repo . ID ) {
2017-10-15 20:36:07 +05:30
return true , nil
}
2021-11-25 10:33:03 +05:30
ownedOrgs , err := GetOrgsCanCreateRepoByUserID ( user . ID )
2021-11-22 20:51:55 +05:30
if err != nil {
2017-10-15 20:36:07 +05:30
return false , err
}
2021-11-22 20:51:55 +05:30
for _ , org := range ownedOrgs {
2021-12-12 21:18:20 +05:30
if repo . OwnerID != org . ID && ! repo_model . HasForkedRepo ( org . ID , repo . ID ) {
2017-10-15 20:36:07 +05:30
return true , nil
}
}
return false , nil
}
2021-12-30 10:33:40 +05:30
// FindUserOrgForks returns the forked repositories for one user from a repository
func FindUserOrgForks ( repoID , userID int64 ) ( [ ] * repo_model . Repository , error ) {
var cond builder . Cond = builder . And (
builder . Eq { "fork_id" : repoID } ,
builder . In ( "owner_id" ,
builder . Select ( "org_id" ) .
From ( "org_user" ) .
Where ( builder . Eq { "uid" : userID } ) ,
) ,
)
var repos [ ] * repo_model . Repository
return repos , db . GetEngine ( db . DefaultContext ) . Table ( "repository" ) . Where ( cond ) . Find ( & repos )
}
2021-12-13 07:29:09 +05:30
// GetForksByUserAndOrgs return forked repos of the user and owned orgs
func GetForksByUserAndOrgs ( user * user_model . User , repo * repo_model . Repository ) ( [ ] * repo_model . Repository , error ) {
var repoList [ ] * repo_model . Repository
if user == nil {
return repoList , nil
}
forkedRepo , err := repo_model . GetUserFork ( repo . ID , user . ID )
if err != nil {
return repoList , err
}
if forkedRepo != nil {
repoList = append ( repoList , forkedRepo )
}
2021-12-30 10:33:40 +05:30
orgForks , err := FindUserOrgForks ( repo . ID , user . ID )
2021-12-13 07:29:09 +05:30
if err != nil {
2021-12-30 10:33:40 +05:30
return nil , err
2021-12-13 07:29:09 +05:30
}
2021-12-30 10:33:40 +05:30
repoList = append ( repoList , orgForks ... )
2021-12-13 07:29:09 +05:30
return repoList , nil
}
2019-10-26 12:24:11 +05:30
// CanUserDelete returns true if user could delete the repository
2021-12-10 06:57:50 +05:30
func CanUserDelete ( repo * repo_model . Repository , user * user_model . User ) ( bool , error ) {
2019-10-26 12:24:11 +05:30
if user . IsAdmin || user . ID == repo . OwnerID {
return true , nil
}
2021-12-10 06:57:50 +05:30
if err := repo . GetOwner ( db . DefaultContext ) ; err != nil {
2019-10-26 12:24:11 +05:30
return false , err
}
if repo . Owner . IsOrganization ( ) {
2021-11-19 17:11:40 +05:30
isOwner , err := OrgFromUser ( repo . Owner ) . IsOwnedBy ( user . ID )
2019-10-26 12:24:11 +05:30
if err != nil {
return false , err
} else if isOwner {
return true , nil
}
}
return false , nil
}
2017-09-14 13:46:22 +05:30
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
2021-12-10 06:57:50 +05:30
func getUsersWithAccessMode ( ctx context . Context , repo * repo_model . Repository , mode perm . AccessMode ) ( _ [ ] * user_model . User , err error ) {
if err = repo . GetOwner ( ctx ) ; err != nil {
2017-09-14 13:46:22 +05:30
return nil , err
}
2021-12-10 06:57:50 +05:30
e := db . GetEngine ( ctx )
2017-09-14 13:46:22 +05:30
accesses := make ( [ ] * Access , 0 , 10 )
if err = e . Where ( "repo_id = ? AND mode >= ?" , repo . ID , mode ) . Find ( & accesses ) ; err != nil {
return nil , err
}
// Leave a seat for owner itself to append later, but if owner is an organization
// and just waste 1 unit is cheaper than re-allocate memory once.
2021-11-24 15:19:20 +05:30
users := make ( [ ] * user_model . User , 0 , len ( accesses ) + 1 )
2017-09-14 13:46:22 +05:30
if len ( accesses ) > 0 {
userIDs := make ( [ ] int64 , len ( accesses ) )
for i := 0 ; i < len ( accesses ) ; i ++ {
userIDs [ i ] = accesses [ i ] . UserID
}
if err = e . In ( "id" , userIDs ) . Find ( & users ) ; err != nil {
return nil , err
}
}
if ! repo . Owner . IsOrganization ( ) {
users = append ( users , repo . Owner )
}
return users , nil
}
2021-12-10 06:57:50 +05:30
// SetRepoReadBy sets repo to be visited by given user.
func SetRepoReadBy ( repoID , userID int64 ) error {
return setRepoNotificationStatusReadIfUnread ( db . GetEngine ( db . DefaultContext ) , userID , repoID )
2014-12-14 03:16:00 +05:30
}
2016-11-28 22:57:55 +05:30
// CreateRepoOptions contains the create repository options
2015-08-28 16:03:09 +05:30
type CreateRepoOptions struct {
2020-01-10 21:05:17 +05:30
Name string
Description string
OriginalURL string
2020-01-21 01:31:19 +05:30
GitServiceType api . GitServiceType
2020-01-10 21:05:17 +05:30
Gitignores string
IssueLabels string
License string
Readme string
2020-03-27 00:44:51 +05:30
DefaultBranch string
2020-01-10 21:05:17 +05:30
IsPrivate bool
IsMirror bool
2020-09-25 10:48:37 +05:30
IsTemplate bool
2020-01-10 21:05:17 +05:30
AutoInit bool
2021-12-10 06:57:50 +05:30
Status repo_model . RepositoryStatus
TrustModel repo_model . TrustModelType
2021-01-03 05:17:47 +05:30
MirrorInterval string
2015-08-28 16:03:09 +05:30
}
2020-01-12 17:41:17 +05:30
// GetRepoInitFile returns repository init files
func GetRepoInitFile ( tp , name string ) ( [ ] byte , error ) {
2018-05-01 07:16:04 +05:30
cleanedName := strings . TrimLeft ( path . Clean ( "/" + name ) , "/" )
2016-12-22 23:42:23 +05:30
relPath := path . Join ( "options" , tp , cleanedName )
2015-08-28 16:03:09 +05:30
// Use custom file when available.
customPath := path . Join ( setting . CustomPath , relPath )
2020-11-28 08:12:08 +05:30
isFile , err := util . IsFile ( customPath )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , customPath , err )
}
if isFile {
2021-09-22 11:08:34 +05:30
return os . ReadFile ( customPath )
2015-08-28 16:03:09 +05:30
}
2016-12-22 23:42:23 +05:30
switch tp {
case "readme" :
return options . Readme ( cleanedName )
case "gitignore" :
return options . Gitignore ( cleanedName )
case "license" :
return options . License ( cleanedName )
2016-12-23 12:48:05 +05:30
case "label" :
return options . Labels ( cleanedName )
2016-12-22 23:42:23 +05:30
default :
return [ ] byte { } , fmt . Errorf ( "Invalid init file type" )
}
2015-08-28 16:03:09 +05:30
}
2020-01-12 17:41:17 +05:30
// CreateRepository creates a repository for the user/organization.
2021-12-10 06:57:50 +05:30
func CreateRepository ( ctx context . Context , doer , u * user_model . User , repo * repo_model . Repository , overwriteOrAdopt bool ) ( err error ) {
2021-12-12 21:18:20 +05:30
if err = repo_model . IsUsableRepoName ( repo . Name ) ; err != nil {
2015-08-08 14:40:34 +05:30
return err
2014-06-19 10:38:03 +05:30
}
2021-12-10 06:57:50 +05:30
has , err := repo_model . IsRepositoryExistCtx ( ctx , u , repo . Name )
2015-03-27 02:41:47 +05:30
if err != nil {
2015-08-08 14:40:34 +05:30
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
2015-03-27 02:41:47 +05:30
} else if has {
2021-12-12 21:18:20 +05:30
return repo_model . ErrRepoAlreadyExist {
Uname : u . Name ,
Name : repo . Name ,
}
2014-06-19 10:38:03 +05:30
}
2021-12-10 06:57:50 +05:30
repoPath := repo_model . RepoPath ( u . Name , repo . Name )
2020-11-28 08:12:08 +05:30
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-25 09:39:23 +05:30
log . Error ( "Files already exist in %s and we are not going to adopt or delete." , repoPath )
2021-12-12 21:18:20 +05:30
return repo_model . ErrRepoFilesAlreadyExist {
2020-09-25 09:39:23 +05:30
Uname : u . Name ,
Name : repo . Name ,
}
}
2021-12-10 06:57:50 +05:30
if err = db . Insert ( ctx , repo ) ; err != nil {
2015-08-08 14:40:34 +05:30
return err
2015-08-29 22:43:24 +05:30
}
2021-12-12 21:18:20 +05:30
if err = repo_model . DeleteRedirect ( ctx , u . ID , repo . Name ) ; err != nil {
2017-02-05 20:05:03 +05:30
return err
}
2015-08-29 22:43:24 +05:30
2017-02-04 21:23:46 +05:30
// insert units for repo
2021-12-10 06:57:50 +05:30
units := make ( [ ] repo_model . RepoUnit , 0 , len ( unit . DefaultRepoUnits ) )
2021-11-10 01:27:58 +05:30
for _ , tp := range unit . DefaultRepoUnits {
if tp == unit . TypeIssues {
2021-12-10 06:57:50 +05:30
units = append ( units , repo_model . RepoUnit {
2017-09-12 12:18:13 +05:30
RepoID : repo . ID ,
Type : tp ,
2021-12-10 06:57:50 +05:30
Config : & repo_model . IssuesConfig {
2018-07-18 02:53:58 +05:30
EnableTimetracker : setting . Service . DefaultEnableTimetracking ,
AllowOnlyContributorsToTrackTime : setting . Service . DefaultAllowOnlyContributorsToTrackTime ,
EnableDependencies : setting . Service . DefaultEnableDependencies ,
} ,
2017-09-12 12:18:13 +05:30
} )
2021-11-10 01:27:58 +05:30
} else if tp == unit . TypePullRequests {
2021-12-10 06:57:50 +05:30
units = append ( units , repo_model . RepoUnit {
2018-07-05 08:32:54 +05:30
RepoID : repo . ID ,
Type : tp ,
2021-12-10 06:57:50 +05:30
Config : & repo_model . PullRequestsConfig { AllowMerge : true , AllowRebase : true , AllowRebaseMerge : true , AllowSquash : true , DefaultMergeStyle : repo_model . MergeStyleMerge } ,
2018-07-05 08:32:54 +05:30
} )
2017-09-12 12:18:13 +05:30
} else {
2021-12-10 06:57:50 +05:30
units = append ( units , repo_model . RepoUnit {
2017-09-12 12:18:13 +05:30
RepoID : repo . ID ,
Type : tp ,
} )
}
2017-02-04 21:23:46 +05:30
}
2021-12-10 06:57:50 +05:30
if err = db . Insert ( ctx , units ) ; err != nil {
2017-02-04 21:23:46 +05:30
return err
}
2015-08-29 22:43:24 +05:30
// Remember visibility preference.
u . LastRepoVisibility = repo . IsPrivate
2021-11-24 15:19:20 +05:30
if err = user_model . UpdateUserColsEngine ( db . GetEngine ( ctx ) , u , "last_repo_visibility" ) ; err != nil {
2015-08-29 22:43:24 +05:30
return fmt . Errorf ( "updateUser: %v" , err )
2015-08-08 14:40:34 +05:30
}
2021-11-24 15:19:20 +05:30
if _ , err = db . GetEngine ( ctx ) . Incr ( "num_repos" ) . ID ( u . ID ) . Update ( new ( user_model . User ) ) ; err != nil {
2019-07-17 23:04:13 +05:30
return fmt . Errorf ( "increment user total_repos: %v" , err )
}
u . NumRepos ++
2019-11-06 15:07:14 +05:30
// Give access to all members in teams with access to all repositories.
2015-08-08 14:40:34 +05:30
if u . IsOrganization ( ) {
2021-11-19 17:11:40 +05:30
teams , err := OrgFromUser ( u ) . loadTeams ( db . GetEngine ( ctx ) )
if err != nil {
2021-08-12 18:13:08 +05:30
return fmt . Errorf ( "loadTeams: %v" , err )
2019-10-26 12:24:11 +05:30
}
2021-11-19 17:11:40 +05:30
for _ , t := range teams {
2019-11-06 15:07:14 +05:30
if t . IncludesAllRepositories {
2021-12-10 06:57:50 +05:30
if err := t . addRepository ( ctx , repo ) ; err != nil {
2019-11-06 15:07:14 +05:30
return fmt . Errorf ( "addRepository: %v" , err )
}
}
2015-08-08 14:40:34 +05:30
}
2019-11-20 16:57:49 +05:30
2021-09-23 21:15:36 +05:30
if isAdmin , err := isUserRepoAdmin ( db . GetEngine ( ctx ) , repo , doer ) ; err != nil {
2019-11-20 16:57:49 +05:30
return fmt . Errorf ( "isUserRepoAdmin: %v" , err )
} else if ! isAdmin {
2022-01-10 15:02:37 +05:30
// Make creator repo admin if it wasn't assigned automatically
2021-12-10 06:57:50 +05:30
if err = addCollaborator ( ctx , repo , doer ) ; err != nil {
2019-11-20 16:57:49 +05:30
return fmt . Errorf ( "AddCollaborator: %v" , err )
}
2021-12-10 06:57:50 +05:30
if err = changeCollaborationAccessMode ( db . GetEngine ( ctx ) , repo , doer . ID , perm . AccessModeAdmin ) ; err != nil {
2019-11-20 16:57:49 +05:30
return fmt . Errorf ( "ChangeCollaborationAccessMode: %v" , err )
}
}
2021-12-10 06:57:50 +05:30
} else if err = recalculateAccesses ( ctx , repo ) ; err != nil {
2015-08-08 14:40:34 +05:30
// Organization automatically called this in addRepository method.
2019-06-13 01:11:28 +05:30
return fmt . Errorf ( "recalculateAccesses: %v" , err )
2015-08-08 14:40:34 +05:30
}
2019-01-27 14:55:21 +05:30
if setting . Service . AutoWatchNewRepos {
2021-12-12 21:18:20 +05:30
if err = repo_model . WatchRepoCtx ( ctx , doer . ID , repo . ID , true ) ; err != nil {
2019-01-27 14:55:21 +05:30
return fmt . Errorf ( "watchRepo: %v" , err )
}
}
2015-08-08 14:40:34 +05:30
2021-11-10 10:43:16 +05:30
if err = webhook . CopyDefaultWebhooksToRepo ( ctx , repo . ID ) ; err != nil {
2019-03-19 08:03:20 +05:30
return fmt . Errorf ( "copyDefaultWebhooksToRepo: %v" , err )
}
2015-08-08 14:40:34 +05:30
return nil
}
2021-10-14 01:17:02 +05:30
// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
2021-12-10 06:57:50 +05:30
func CheckDaemonExportOK ( ctx context . Context , repo * repo_model . Repository ) error {
if err := repo . GetOwner ( ctx ) ; err != nil {
2021-10-14 01:17:02 +05:30
return err
}
// Create/Remove git-daemon-export-ok for git-daemon...
daemonExportFile := path . Join ( repo . RepoPath ( ) , ` git-daemon-export-ok ` )
isExist , err := util . IsExist ( daemonExportFile )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , daemonExportFile , err )
return err
}
isPublic := ! repo . IsPrivate && repo . Owner . Visibility == api . VisibleTypePublic
if ! isPublic && isExist {
if err = util . Remove ( daemonExportFile ) ; err != nil {
log . Error ( "Failed to remove %s: %v" , daemonExportFile , err )
}
} else if isPublic && ! isExist {
if f , err := os . Create ( daemonExportFile ) ; err != nil {
log . Error ( "Failed to create %s: %v" , daemonExportFile , err )
} else {
f . Close ( )
}
}
return nil
}
2020-01-12 17:41:17 +05:30
// IncrementRepoForkNum increment repository fork number
2021-09-23 21:15:36 +05:30
func IncrementRepoForkNum ( ctx context . Context , repoID int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , repoID )
2020-01-12 17:41:17 +05:30
return err
}
2021-09-14 22:37:08 +05:30
// DecrementRepoForkNum decrement repository fork number
2021-09-23 21:15:36 +05:30
func DecrementRepoForkNum ( ctx context . Context , repoID int64 ) error {
_ , err := db . GetEngine ( ctx ) . Exec ( "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repoID )
2021-09-14 22:37:08 +05:30
return err
}
2021-12-10 06:57:50 +05:30
func updateRepository ( ctx context . Context , repo * repo_model . Repository , visibilityChanged bool ) ( err error ) {
2014-04-04 01:20:55 +05:30
repo . LowerName = strings . ToLower ( repo . Name )
2020-07-08 03:05:52 +05:30
if utf8 . RuneCountInString ( repo . Description ) > 255 {
repo . Description = string ( [ ] rune ( repo . Description ) [ : 255 ] )
2014-03-23 01:30:46 +05:30
}
2020-07-08 03:05:52 +05:30
if utf8 . RuneCountInString ( repo . Website ) > 255 {
repo . Website = string ( [ ] rune ( repo . Website ) [ : 255 ] )
2014-03-23 01:30:46 +05:30
}
2015-03-16 14:22:11 +05:30
2021-12-10 06:57:50 +05:30
e := db . GetEngine ( ctx )
2017-10-05 10:13:04 +05:30
if _ , err = e . ID ( repo . ID ) . AllCols ( ) . Update ( repo ) ; err != nil {
2015-03-16 14:22:11 +05:30
return fmt . Errorf ( "update: %v" , err )
}
2021-12-10 06:57:50 +05:30
if err = updateRepoSize ( e , repo ) ; err != nil {
2020-11-07 10:14:08 +05:30
log . Error ( "Failed to update size for repository: %v" , err )
}
2015-03-16 14:22:11 +05:30
if visibilityChanged {
2021-12-10 06:57:50 +05:30
if err = repo . GetOwner ( ctx ) ; err != nil {
2015-03-16 14:22:11 +05:30
return fmt . Errorf ( "getOwner: %v" , err )
}
2015-10-09 08:08:42 +05:30
if repo . Owner . IsOrganization ( ) {
2017-01-05 06:20:34 +05:30
// Organization repository need to recalculate access table when visibility is changed.
2021-12-10 06:57:50 +05:30
if err = recalculateTeamAccesses ( ctx , repo , 0 ) ; err != nil {
2015-10-09 08:08:42 +05:30
return fmt . Errorf ( "recalculateTeamAccesses: %v" , err )
}
2015-03-16 14:22:11 +05:30
}
2015-09-01 21:13:53 +05:30
2017-02-11 16:27:57 +05:30
// If repo has become private, we need to set its actions to private.
if repo . IsPrivate {
_ , err = e . Where ( "repo_id = ?" , repo . ID ) . Cols ( "is_private" ) . Update ( & Action {
IsPrivate : true ,
} )
if err != nil {
return err
}
}
2016-08-11 08:38:09 +05:30
// Create/Remove git-daemon-export-ok for git-daemon...
2021-12-10 06:57:50 +05:30
if err := CheckDaemonExportOK ( db . WithEngine ( ctx , e ) , repo ) ; err != nil {
2020-11-28 08:12:08 +05:30
return err
}
2016-08-11 08:38:09 +05:30
2021-12-12 21:18:20 +05:30
forkRepos , err := repo_model . GetRepositoriesByForkID ( ctx , repo . ID )
2015-09-01 21:13:53 +05:30
if err != nil {
return fmt . Errorf ( "getRepositoriesByForkID: %v" , err )
}
for i := range forkRepos {
2020-06-07 06:15:12 +05:30
forkRepos [ i ] . IsPrivate = repo . IsPrivate || repo . Owner . Visibility == api . VisibleTypePrivate
2021-12-10 06:57:50 +05:30
if err = updateRepository ( ctx , forkRepos [ i ] , true ) ; err != nil {
2015-09-01 21:13:53 +05:30
return fmt . Errorf ( "updateRepository[%d]: %v" , forkRepos [ i ] . ID , err )
}
}
2015-03-16 14:22:11 +05:30
}
return nil
2014-03-22 14:14:57 +05:30
}
2020-01-12 17:41:17 +05:30
// UpdateRepositoryCtx updates a repository with db context
2021-12-10 06:57:50 +05:30
func UpdateRepositoryCtx ( ctx context . Context , repo * repo_model . Repository , visibilityChanged bool ) error {
return updateRepository ( ctx , repo , visibilityChanged )
2020-01-12 17:41:17 +05:30
}
2016-11-28 22:57:55 +05:30
// UpdateRepository updates a repository
2021-12-10 06:57:50 +05:30
func UpdateRepository ( repo * repo_model . Repository , visibilityChanged bool ) ( err error ) {
2021-11-21 21:11:00 +05:30
ctx , committer , err := db . TxContext ( )
if err != nil {
2015-03-16 14:22:11 +05:30
return err
}
2021-11-21 21:11:00 +05:30
defer committer . Close ( )
2015-03-16 14:22:11 +05:30
2021-12-10 06:57:50 +05:30
if err = updateRepository ( ctx , repo , visibilityChanged ) ; err != nil {
2015-03-16 14:22:11 +05:30
return fmt . Errorf ( "updateRepository: %v" , err )
}
2021-11-21 21:11:00 +05:30
return committer . Commit ( )
2015-02-13 11:28:46 +05:30
}
2014-12-07 06:52:48 +05:30
// DeleteRepository deletes a repository for a user or organization.
2021-01-19 01:30:50 +05:30
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
2021-11-24 15:19:20 +05:30
func DeleteRepository ( doer * user_model . User , uid , repoID int64 ) error {
2021-11-21 21:11:00 +05:30
ctx , committer , err := db . TxContext ( )
if err != nil {
2021-01-19 01:30:50 +05:30
return err
}
2021-11-21 21:11:00 +05:30
defer committer . Close ( )
sess := db . GetEngine ( ctx )
2021-01-19 01:30:50 +05:30
2014-08-27 14:09:36 +05:30
// In case is a organization.
2021-11-24 15:19:20 +05:30
org , err := user_model . GetUserByIDEngine ( sess , uid )
2014-08-27 14:09:36 +05:30
if err != nil {
return err
}
2021-12-10 06:57:50 +05:30
repo := & repo_model . Repository { OwnerID : uid }
2021-01-19 01:30:50 +05:30
has , err := sess . ID ( repoID ) . Get ( repo )
2017-03-01 09:35:45 +05:30
if err != nil {
return err
} else if ! has {
2021-12-10 06:57:50 +05:30
return repo_model . ErrRepoNotExist {
ID : repoID ,
UID : uid ,
OwnerName : "" ,
Name : "" ,
}
2017-03-01 09:35:45 +05:30
}
2019-02-04 05:26:53 +05:30
// Delete Deploy Keys
2021-12-10 13:44:24 +05:30
deployKeys , err := asymkey_model . ListDeployKeys ( ctx , & asymkey_model . ListDeployKeysOptions { RepoID : repoID } )
2019-02-04 05:26:53 +05:30
if err != nil {
return fmt . Errorf ( "listDeployKeys: %v" , err )
}
2022-01-20 23:16:10 +05:30
needRewriteKeysFile := len ( deployKeys ) > 0
2019-02-04 05:26:53 +05:30
for _ , dKey := range deployKeys {
2021-12-10 13:44:24 +05:30
if err := DeleteDeployKey ( ctx , doer , dKey . ID ) ; err != nil {
2019-02-04 05:26:53 +05:30
return fmt . Errorf ( "deleteDeployKeys: %v" , err )
}
}
2021-12-10 06:57:50 +05:30
if cnt , err := sess . ID ( repoID ) . Delete ( & repo_model . Repository { } ) ; err != nil {
2017-03-01 09:35:45 +05:30
return err
} else if cnt != 1 {
2021-12-10 06:57:50 +05:30
return repo_model . ErrRepoNotExist {
ID : repoID ,
UID : uid ,
OwnerName : "" ,
Name : "" ,
}
2017-03-01 09:35:45 +05:30
}
2014-08-27 14:09:36 +05:30
if org . IsOrganization ( ) {
2021-11-19 17:11:40 +05:30
teams , err := OrgFromUser ( org ) . loadTeams ( sess )
if err != nil {
return err
}
for _ , t := range teams {
2015-02-23 12:45:53 +05:30
if ! t . hasRepository ( sess , repoID ) {
2014-08-27 14:09:36 +05:30
continue
2021-12-10 06:57:50 +05:30
} else if err = t . removeRepository ( ctx , repo , false ) ; err != nil {
2014-08-27 14:09:36 +05:30
return err
}
}
}
2021-11-19 19:09:57 +05:30
attachments := make ( [ ] * repo_model . Attachment , 0 , 20 )
2019-12-12 11:01:05 +05:30
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-18 09:53:45 +05:30
releaseAttachments = append ( releaseAttachments , attachments [ i ] . RelativePath ( ) )
2019-12-12 11:01:05 +05:30
}
2021-03-20 00:31:24 +05:30
if _ , err := sess . Exec ( "UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)" , repo . ID ) ; err != nil {
2020-07-08 00:46:34 +05:30
return err
}
2021-03-20 00:31:24 +05:30
if err := deleteBeans ( sess ,
2015-12-01 07:15:55 +05:30
& Access { RepoID : repo . ID } ,
& Action { RepoID : repo . ID } ,
& Collaboration { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& Comment { RefRepoID : repoID } ,
& CommitStatus { RepoID : repoID } ,
& DeletedBranch { RepoID : repoID } ,
2021-11-10 10:43:16 +05:30
& webhook . HookTask { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& LFSLock { RepoID : repoID } ,
2021-12-10 06:57:50 +05:30
& repo_model . LanguageStat { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& Milestone { RepoID : repoID } ,
2021-12-10 06:57:50 +05:30
& repo_model . Mirror { RepoID : repoID } ,
2018-12-11 01:31:01 +05:30
& Notification { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& ProtectedBranch { RepoID : repoID } ,
2021-06-25 19:58:55 +05:30
& ProtectedTag { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& PullRequest { BaseRepoID : repoID } ,
2021-12-10 06:57:50 +05:30
& repo_model . PushMirror { RepoID : repoID } ,
2021-05-01 00:40:39 +05:30
& Release { RepoID : repoID } ,
2021-12-10 06:57:50 +05:30
& repo_model . RepoIndexerStatus { RepoID : repoID } ,
2021-12-12 21:18:20 +05:30
& repo_model . Redirect { RedirectRepoID : repoID } ,
2021-12-10 06:57:50 +05:30
& repo_model . RepoUnit { RepoID : repoID } ,
2021-12-12 21:18:20 +05:30
& repo_model . Star { RepoID : repoID } ,
2019-10-13 18:53:14 +05:30
& Task { RepoID : repoID } ,
2021-12-12 21:18:20 +05:30
& repo_model . Watch { RepoID : repoID } ,
2021-11-10 10:43:16 +05:30
& webhook . Webhook { RepoID : repoID } ,
2015-12-01 07:15:55 +05:30
) ; err != nil {
return fmt . Errorf ( "deleteBeans: %v" , err )
2014-05-14 22:34:57 +05:30
}
2021-03-20 00:31:24 +05:30
// Delete Labels and related objects
if err := deleteLabelsByRepoID ( sess , repoID ) ; err != nil {
return err
}
2020-05-29 18:54:15 +05:30
// Delete Issues and related objects
var attachmentPaths [ ] string
if attachmentPaths , err = deleteIssuesByRepoID ( sess , repoID ) ; err != nil {
2018-11-30 18:29:12 +05:30
return err
2014-05-14 18:53:33 +05:30
}
2014-10-19 11:05:24 +05:30
2021-06-14 07:52:55 +05:30
// Delete issue index
2021-09-19 17:19:59 +05:30
if err := db . DeleteResouceIndex ( sess , "issue_index" , repoID ) ; err != nil {
2021-06-14 07:52:55 +05:30
return err
}
2014-10-14 00:53:30 +05:30
if repo . IsFork {
2021-03-20 00:31:24 +05:30
if _ , err := sess . Exec ( "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repo . ForkID ) ; err != nil {
2015-09-01 21:13:53 +05:30
return fmt . Errorf ( "decrease fork count: %v" , err )
2014-10-19 11:05:24 +05:30
}
2014-10-14 00:53:30 +05:30
}
2014-04-13 06:05:35 +05:30
2021-03-20 00:31:24 +05:30
if _ , err := sess . Exec ( "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?" , uid ) ; err != nil {
2014-04-13 06:05:35 +05:30
return err
}
2014-10-09 03:59:18 +05:30
2020-01-31 12:27:19 +05:30
if len ( repo . Topics ) > 0 {
2021-12-12 21:18:20 +05:30
if err := repo_model . RemoveTopicsFromRepo ( ctx , repo . ID ) ; err != nil {
2020-01-31 12:27:19 +05:30
return err
}
}
2020-08-17 08:37:38 +05:30
projects , _ , err := getProjects ( sess , ProjectSearchOptions {
RepoID : repoID ,
} )
if err != nil {
return fmt . Errorf ( "get projects: %v" , err )
}
for i := range projects {
if err := deleteProjectByID ( sess , projects [ i ] . ID ) ; err != nil {
return fmt . Errorf ( "delete project [%d]: %v" , projects [ i ] . ID , err )
}
}
2016-12-26 06:46:37 +05:30
// Remove LFS objects
var lfsObjects [ ] * LFSMetaObject
if err = sess . Where ( "repository_id=?" , repoID ) . Find ( & lfsObjects ) ; err != nil {
return err
}
2022-01-20 23:16:10 +05:30
lfsPaths := make ( [ ] string , 0 , len ( lfsObjects ) )
2016-12-26 06:46:37 +05:30
for _ , v := range lfsObjects {
2021-04-09 03:55:57 +05:30
count , err := sess . Count ( & LFSMetaObject { Pointer : lfs . Pointer { Oid : v . Oid } } )
2016-12-26 06:46:37 +05:30
if err != nil {
return err
}
if count > 1 {
continue
}
2021-09-08 20:49:30 +05:30
lfsPaths = append ( lfsPaths , v . RelativePath ( ) )
2016-12-26 06:46:37 +05:30
}
if _ , err := sess . Delete ( & LFSMetaObject { RepositoryID : repoID } ) ; err != nil {
return err
}
2021-06-24 02:42:38 +05:30
// Remove archives
2021-12-06 12:49:28 +05:30
var archives [ ] * repo_model . RepoArchiver
2021-06-24 02:42:38 +05:30
if err = sess . Where ( "repo_id=?" , repoID ) . Find ( & archives ) ; err != nil {
return err
}
2022-01-20 23:16:10 +05:30
archivePaths := make ( [ ] string , 0 , len ( archives ) )
2021-06-24 02:42:38 +05:30
for _ , v := range archives {
p , _ := v . RelativePath ( )
2021-09-08 20:49:30 +05:30
archivePaths = append ( archivePaths , p )
2021-06-24 02:42:38 +05:30
}
2021-12-06 12:49:28 +05:30
if _ , err := sess . Delete ( & repo_model . RepoArchiver { RepoID : repoID } ) ; err != nil {
2021-06-24 02:42:38 +05:30
return err
}
2015-09-01 21:13:53 +05:30
if repo . NumForks > 0 {
2017-01-27 21:41:41 +05:30
if _ , err = sess . Exec ( "UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?" , false , repo . ID ) ; err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "reset 'fork_id' and 'is_fork': %v" , err )
2016-07-09 11:12:05 +05:30
}
2015-09-01 21:13:53 +05:30
}
2021-09-08 20:49:30 +05:30
// Get all attachments with both issue_id and release_id are zero
2021-11-19 19:09:57 +05:30
var newAttachments [ ] * repo_model . Attachment
2021-09-08 20:49:30 +05:30
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 23:16:10 +05:30
newAttachmentPaths := make ( [ ] string , 0 , len ( newAttachments ) )
2021-09-08 20:49:30 +05:30
for _ , attach := range newAttachments {
newAttachmentPaths = append ( newAttachmentPaths , attach . RelativePath ( ) )
}
2021-11-19 19:09:57 +05:30
if _ , err := sess . Where ( "repo_id=?" , repo . ID ) . Delete ( new ( repo_model . Attachment ) ) ; err != nil {
2021-09-08 20:49:30 +05:30
return err
}
2021-11-21 21:11:00 +05:30
if err = committer . Commit ( ) ; err != nil {
2021-01-19 01:30:50 +05:30
return err
2017-01-27 21:41:41 +05:30
}
2021-11-21 21:11:00 +05:30
committer . Close ( )
2019-12-12 11:01:05 +05:30
2021-12-10 13:44:24 +05:30
if needRewriteKeysFile {
if err := asymkey_model . RewriteAllPublicKeys ( ) ; err != nil {
log . Error ( "RewriteAllPublicKeys failed: %v" , err )
}
}
2019-12-12 11:01:05 +05:30
// We should always delete the files after the database transaction succeed. If
2021-07-08 17:08:13 +05:30
// we delete the file but the database rollback, the repository will be broken.
2019-12-12 11:01:05 +05:30
2021-09-08 20:49:30 +05:30
// Remove repository files.
repoPath := repo . RepoPath ( )
2021-11-18 23:12:27 +05:30
admin_model . RemoveAllWithNotice ( db . DefaultContext , "Delete repository files" , repoPath )
2021-09-08 20:49:30 +05:30
// Remove wiki files
if repo . HasWiki ( ) {
2021-11-18 23:12:27 +05:30
admin_model . RemoveAllWithNotice ( db . DefaultContext , "Delete repository wiki" , repo . WikiPath ( ) )
2021-09-08 20:49:30 +05:30
}
// Remove archives
for i := range archivePaths {
2021-11-18 23:12:27 +05:30
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . RepoArchives , "Delete repo archive file" , archivePaths [ i ] )
2021-09-08 20:49:30 +05:30
}
// Remove lfs objects
for i := range lfsPaths {
2021-11-18 23:12:27 +05:30
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . LFS , "Delete orphaned LFS file" , lfsPaths [ i ] )
2021-09-08 20:49:30 +05:30
}
2019-12-12 11:01:05 +05:30
// Remove issue attachment files.
for i := range attachmentPaths {
2021-11-18 23:12:27 +05:30
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete issue attachment" , attachmentPaths [ i ] )
2019-12-12 11:01:05 +05:30
}
// Remove release attachment files.
for i := range releaseAttachments {
2021-11-18 23:12:27 +05:30
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete release attachment" , releaseAttachments [ i ] )
2019-12-12 11:01:05 +05:30
}
2021-09-08 20:49:30 +05:30
// Remove attachment with no issue_id and release_id.
for i := range newAttachmentPaths {
2021-11-18 23:12:27 +05:30
admin_model . RemoveStorageWithNotice ( db . DefaultContext , storage . Attachments , "Delete issue attachment" , attachmentPaths [ i ] )
2021-09-08 20:49:30 +05:30
}
2019-05-30 07:52:26 +05:30
if len ( repo . Avatar ) > 0 {
2020-10-14 18:37:51 +05:30
if err := storage . RepoAvatars . Delete ( repo . CustomAvatarRelativePath ( ) ) ; err != nil {
return fmt . Errorf ( "Failed to remove %s: %v" , repo . Avatar , err )
2019-05-30 07:52:26 +05:30
}
}
2015-09-01 21:13:53 +05:30
return nil
2014-03-21 01:34:56 +05:30
}
2015-09-01 21:13:53 +05:30
type repoChecker struct {
2022-01-18 00:01:58 +05:30
querySQL func ( ctx context . Context ) ( [ ] map [ string ] [ ] byte , error )
correctSQL func ( ctx context . Context , id int64 ) error
desc string
2015-09-01 21:13:53 +05:30
}
2015-08-18 01:33:11 +05:30
2019-12-15 15:21:28 +05:30
func repoStatsCheck ( ctx context . Context , checker * repoChecker ) {
2022-01-18 00:01:58 +05:30
results , err := checker . querySQL ( ctx )
2015-08-18 01:33:11 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "Select %s: %v" , checker . desc , err )
2015-08-18 01:33:11 +05:30
return
}
2015-09-01 21:13:53 +05:30
for _ , result := range results {
2020-12-25 15:29:32 +05:30
id , _ := strconv . ParseInt ( string ( result [ "id" ] ) , 10 , 64 )
2019-12-15 15:21:28 +05:30
select {
case <- ctx . Done ( ) :
2022-01-18 00:01:58 +05:30
log . Warn ( "CheckRepoStats: Cancelled before checking %s for with id=%d" , checker . desc , id )
2019-12-15 15:21:28 +05:30
return
default :
}
2015-09-01 21:13:53 +05:30
log . Trace ( "Updating %s: %d" , checker . desc , id )
2022-01-18 00:01:58 +05:30
err = checker . correctSQL ( ctx , id )
2015-08-18 01:33:11 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "Update %s[%d]: %v" , checker . desc , id , err )
2015-08-18 01:33:11 +05:30
}
}
2015-09-01 21:13:53 +05:30
}
2015-08-18 01:33:11 +05:30
2022-01-18 00:01:58 +05:30
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 milestoneStatsCorrectNumIssues ( ctx context . Context , id int64 ) error {
return updateMilestoneCounters ( ctx , 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 )
err = milestoneStatsCorrectNumIssues ( ctx , id )
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 22:57:55 +05:30
// CheckRepoStats checks the repository stats
2020-05-17 05:01:38 +05:30
func CheckRepoStats ( ctx context . Context ) error {
2015-09-01 21:13:53 +05:30
log . Trace ( "Doing: CheckRepoStats" )
2015-08-29 22:43:24 +05:30
2015-09-01 21:13:53 +05:30
checkers := [ ] * repoChecker {
// Repository.NumWatches
{
2022-01-18 00:01:58 +05:30
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 21:13:53 +05:30
"repository count 'num_watches'" ,
} ,
// Repository.NumStars
{
2022-01-18 00:01:58 +05:30
statsQuery ( "SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)" ) ,
repoStatsCorrectNumStars ,
2015-09-01 21:13:53 +05:30
"repository count 'num_stars'" ,
} ,
2022-01-18 00:01:58 +05:30
// 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 21:13:53 +05:30
// Label.NumIssues
{
2022-01-18 00:01:58 +05:30
statsQuery ( "SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)" ) ,
labelStatsCorrectNumIssues ,
2015-09-01 21:13:53 +05:30
"label count 'num_issues'" ,
} ,
2022-01-18 00:01:58 +05:30
// 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 ) ,
milestoneStatsCorrectNumIssues ,
"milestone count 'num_closed_issues' and 'num_issues'" ,
} ,
2015-09-01 21:13:53 +05:30
// User.NumRepos
{
2022-01-18 00:01:58 +05:30
statsQuery ( "SELECT `user`.id FROM `user` WHERE `user`.num_repos!=(SELECT COUNT(*) FROM `repository` WHERE owner_id=`user`.id)" ) ,
userStatsCorrectNumRepos ,
2015-09-01 21:13:53 +05:30
"user count 'num_repos'" ,
} ,
2015-10-30 06:10:57 +05:30
// Issue.NumComments
{
2022-01-18 00:01:58 +05:30
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-30 06:10:57 +05:30
"issue count 'num_comments'" ,
} ,
2015-09-01 21:13:53 +05:30
}
2020-05-17 05:01:38 +05:30
for _ , checker := range checkers {
2019-12-15 15:21:28 +05:30
select {
case <- ctx . Done ( ) :
2020-05-17 05:01:38 +05:30
log . Warn ( "CheckRepoStats: Cancelled before %s" , checker . desc )
2021-11-10 10:43:16 +05:30
return db . ErrCancelledf ( "before checking %s" , checker . desc )
2019-12-15 15:21:28 +05:30
default :
2020-05-17 05:01:38 +05:30
repoStatsCheck ( ctx , checker )
2019-12-15 15:21:28 +05:30
}
2015-09-01 21:13:53 +05:30
}
2016-05-28 06:53:39 +05:30
// FIXME: use checker when stop supporting old fork repo format.
2015-09-01 21:13:53 +05:30
// ***** START: Repository.NumForks *****
2022-01-18 00:01:58 +05:30
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 22:43:24 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "Select repository count 'num_forks': %v" , err )
2015-09-01 21:13:53 +05:30
} else {
for _ , result := range results {
2020-12-25 15:29:32 +05:30
id , _ := strconv . ParseInt ( string ( result [ "id" ] ) , 10 , 64 )
2019-12-15 15:21:28 +05:30
select {
case <- ctx . Done ( ) :
2020-05-17 05:01:38 +05:30
log . Warn ( "CheckRepoStats: Cancelled" )
2022-01-18 00:01:58 +05:30
return db . ErrCancelledf ( "during repository count 'num_fork' for repo ID %d" , id )
2019-12-15 15:21:28 +05:30
default :
}
2015-09-01 21:13:53 +05:30
log . Trace ( "Updating repository count 'num_forks': %d" , id )
2021-12-10 06:57:50 +05:30
repo , err := repo_model . GetRepositoryByID ( id )
2015-09-01 21:13:53 +05:30
if err != nil {
2021-12-10 06:57:50 +05:30
log . Error ( "repo_model.GetRepositoryByID[%d]: %v" , id , err )
2015-09-01 21:13:53 +05:30
continue
}
2021-09-23 21:15:36 +05:30
rawResult , err := db . GetEngine ( db . DefaultContext ) . Query ( "SELECT COUNT(*) FROM `repository` WHERE fork_id=?" , repo . ID )
2015-09-01 21:13:53 +05:30
if err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "Select count of forks[%d]: %v" , repo . ID , err )
2015-09-01 21:13:53 +05:30
continue
}
repo . NumForks = int ( parseCountResult ( rawResult ) )
if err = UpdateRepository ( repo , false ) ; err != nil {
2019-04-02 13:18:31 +05:30
log . Error ( "UpdateRepository[%d]: %v" , id , err )
2015-09-01 21:13:53 +05:30
continue
}
2015-08-29 22:43:24 +05:30
}
}
2015-09-01 21:13:53 +05:30
// ***** END: Repository.NumForks *****
2020-05-17 05:01:38 +05:30
return nil
2015-03-21 18:25:00 +05:30
}
2022-01-18 00:01:58 +05:30
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 15:19:20 +05:30
func updateUserStarNumbers ( users [ ] user_model . User ) error {
2021-11-21 21:11:00 +05:30
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-08 00:46:34 +05:30
// DoctorUserStarNum recalculate Stars number for all user
func DoctorUserStarNum ( ) ( err error ) {
const batchSize = 100
for start := 0 ; ; start += batchSize {
2021-11-24 15:19:20 +05:30
users := make ( [ ] user_model . User , 0 , batchSize )
2021-11-21 21:11:00 +05:30
if err = db . GetEngine ( db . DefaultContext ) . Limit ( batchSize , start ) . Where ( "type = ?" , 0 ) . Cols ( "id" ) . Find ( & users ) ; err != nil {
2020-07-08 00:46:34 +05:30
return
}
if len ( users ) == 0 {
break
}
2021-11-21 21:11:00 +05:30
if err = updateUserStarNumbers ( users ) ; err != nil {
2020-07-08 00:46:34 +05:30
return
}
}
log . Debug ( "recalculate Stars number for all user finished" )
return
}
2020-10-14 18:37:51 +05:30
2021-11-19 19:09:57 +05:30
// LinkedRepository returns the linked repo if any
2021-12-10 06:57:50 +05:30
func LinkedRepository ( a * repo_model . Attachment ) ( * repo_model . Repository , unit . Type , error ) {
2021-11-19 19:09:57 +05:30
if a . IssueID != 0 {
iss , err := GetIssueByID ( a . IssueID )
if err != nil {
return nil , unit . TypeIssues , err
}
2021-12-10 06:57:50 +05:30
repo , err := repo_model . GetRepositoryByID ( iss . RepoID )
2021-11-19 19:09:57 +05:30
unitType := unit . TypeIssues
if iss . IsPull {
unitType = unit . TypePullRequests
}
return repo , unitType , err
} else if a . ReleaseID != 0 {
rel , err := GetReleaseByID ( a . ReleaseID )
if err != nil {
return nil , unit . TypeReleases , err
}
2021-12-10 06:57:50 +05:30
repo , err := repo_model . GetRepositoryByID ( rel . RepoID )
2021-11-19 19:09:57 +05:30
return repo , unit . TypeReleases , err
}
return nil , - 1 , nil
}
2021-12-10 13:44:24 +05:30
// 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 )
}
sess := db . GetEngine ( ctx )
// 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 )
}
has , err := isUserRepoAdmin ( sess , repo , doer )
if err != nil {
return fmt . Errorf ( "GetUserRepoPermission: %v" , err )
} else if ! has {
return asymkey_model . ErrKeyAccessDenied {
UserID : doer . ID ,
KeyID : key . ID ,
Note : "deploy" ,
}
}
}
if _ , err = sess . ID ( key . ID ) . Delete ( new ( asymkey_model . DeployKey ) ) ; err != nil {
return fmt . Errorf ( "delete deploy key [%d]: %v" , key . ID , err )
}
// Check if this is the last reference to same key content.
has , err := sess .
Where ( "key_id = ?" , key . KeyID ) .
Get ( new ( asymkey_model . DeployKey ) )
if err != nil {
return err
} else if ! has {
if err = asymkey_model . DeletePublicKeys ( ctx , key . KeyID ) ; err != nil {
return err
}
}
return nil
}