2014-02-15 03:16:54 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-14 18:20:57 +04:00
package models
import (
2014-03-10 04:06:29 +04:00
"errors"
2014-03-11 04:48:58 +04:00
"fmt"
2014-03-11 08:18:44 +04:00
"io/ioutil"
2014-02-14 18:20:57 +04:00
"os"
"path/filepath"
"strings"
2014-03-17 19:56:50 +04:00
"sync"
2014-02-14 18:20:57 +04:00
"time"
2014-03-11 08:53:53 +04:00
"unicode/utf8"
2014-02-14 18:20:57 +04:00
2014-03-17 19:56:50 +04:00
"github.com/Unknwon/cae/zip"
2014-03-11 09:32:36 +04:00
"github.com/Unknwon/com"
2014-03-17 19:56:50 +04:00
"github.com/gogits/git"
2014-03-08 02:22:15 +04:00
2014-03-11 09:32:36 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-08 02:22:15 +04:00
"github.com/gogits/gogs/modules/log"
2014-02-14 18:20:57 +04:00
)
2014-03-10 04:06:29 +04:00
type Repository struct {
Id int64
OwnerId int64 ` xorm:"unique(s)" `
ForkId int64
LowerName string ` xorm:"unique(s) index not null" `
Name string ` xorm:"index not null" `
Description string
Private bool
NumWatchs int
NumStars int
NumForks int
Created time . Time ` xorm:"created" `
Updated time . Time ` xorm:"updated" `
2014-02-14 18:20:57 +04:00
}
2014-03-02 17:58:20 +04:00
type Star struct {
Id int64
RepoId int64
UserId int64
Created time . Time ` xorm:"created" `
}
2014-03-11 09:32:36 +04:00
var (
2014-03-17 19:56:50 +04:00
gitInitLocker = sync . Mutex { }
2014-03-11 09:32:36 +04:00
LanguageIgns , Licenses [ ] string
)
2014-03-10 04:06:29 +04:00
var (
ErrRepoAlreadyExist = errors . New ( "Repository already exist" )
2014-03-13 06:27:11 +04:00
ErrRepoNotExist = errors . New ( "Repository does not exist" )
2014-03-10 04:06:29 +04:00
)
2014-03-11 09:32:36 +04:00
func init ( ) {
LanguageIgns = strings . Split ( base . Cfg . MustValue ( "repository" , "LANG_IGNS" ) , "|" )
Licenses = strings . Split ( base . Cfg . MustValue ( "repository" , "LICENSES" ) , "|" )
2014-03-17 19:56:50 +04:00
zip . Verbose = false
2014-03-11 09:32:36 +04:00
}
2014-02-14 18:20:57 +04:00
// check if repository is exist
2014-03-10 04:06:29 +04:00
func IsRepositoryExist ( user * User , repoName string ) ( bool , error ) {
repo := Repository { OwnerId : user . Id }
has , err := orm . Where ( "lower_name = ?" , strings . ToLower ( repoName ) ) . Get ( & repo )
2014-02-19 13:50:53 +04:00
if err != nil {
return has , err
}
2014-03-10 04:06:29 +04:00
s , err := os . Stat ( RepoPath ( user . Name , repoName ) )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-03-17 19:56:50 +04:00
return false , nil // Error simply means does not exist, but we don't want to show up.
2014-02-19 13:50:53 +04:00
}
return s . IsDir ( ) , nil
2014-02-14 18:20:57 +04:00
}
2014-03-08 02:08:21 +04:00
// CreateRepository creates a repository for given user or orgnaziation.
2014-03-11 09:32:36 +04:00
func CreateRepository ( user * User , repoName , desc , repoLang , license string , private bool , initReadme bool ) ( * Repository , error ) {
2014-03-10 04:06:29 +04:00
isExist , err := IsRepositoryExist ( user , repoName )
2014-02-14 18:20:57 +04:00
if err != nil {
return nil , err
2014-03-10 04:06:29 +04:00
} else if isExist {
return nil , ErrRepoAlreadyExist
}
repo := & Repository {
OwnerId : user . Id ,
Name : repoName ,
LowerName : strings . ToLower ( repoName ) ,
Description : desc ,
Private : private ,
}
2014-03-11 08:53:53 +04:00
f := RepoPath ( user . Name , repoName )
2014-03-11 09:32:36 +04:00
if err = initRepository ( f , user , repo , initReadme , repoLang , license ) ; err != nil {
2014-03-11 08:53:53 +04:00
return nil , err
}
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
defer session . Close ( )
session . Begin ( )
2014-03-10 04:06:29 +04:00
if _ , err = session . Insert ( repo ) ; err != nil {
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return nil , err
}
2014-03-10 04:06:29 +04:00
access := Access {
UserName : user . Name ,
2014-02-25 11:11:54 +04:00
RepoName : repo . Name ,
Mode : AU_WRITABLE ,
}
2014-03-10 04:06:29 +04:00
if _ , err = session . Insert ( & access ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-25 11:11:54 +04:00
}
return nil , err
}
2014-03-10 04:06:29 +04:00
if _ , err = session . Exec ( "update user set num_repos = num_repos + 1 where id = ?" , user . Id ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
return nil , err
}
2014-03-10 04:06:29 +04:00
if err = session . Commit ( ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
return nil , err
}
2014-03-13 09:14:43 +04:00
return repo , NewRepoAction ( user , repo )
2014-03-17 19:56:50 +04:00
return nil , nil
}
// extractGitBareZip extracts git-bare.zip to repository path.
func extractGitBareZip ( repoPath string ) error {
z , err := zip . Open ( "conf/content/git-bare.zip" )
if err != nil {
fmt . Println ( "shi?" )
return err
}
defer z . Close ( )
return z . ExtractTo ( repoPath )
}
// initRepoCommit temporarily changes with work directory.
func initRepoCommit ( tmpPath string , sig * git . Signature ) error {
gitInitLocker . Lock ( )
defer gitInitLocker . Unlock ( )
// Change work directory.
curPath , err := os . Getwd ( )
if err != nil {
return err
} else if err = os . Chdir ( tmpPath ) ; err != nil {
return err
}
defer os . Chdir ( curPath )
if _ , _ , err := com . ExecCmd ( "git" , "add" , "--all" ) ; err != nil {
return err
}
if _ , _ , err := com . ExecCmd ( "git" , "commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) ,
"-m" , "Init commit" ) ; err != nil {
return err
}
if _ , _ , err := com . ExecCmd ( "git" , "push" , "origin" , "master" ) ; err != nil {
return err
}
return nil
2014-02-14 18:20:57 +04:00
}
2014-03-11 04:48:58 +04:00
// InitRepository initializes README and .gitignore if needed.
2014-03-11 09:32:36 +04:00
func initRepository ( f string , user * User , repo * Repository , initReadme bool , repoLang , license string ) error {
2014-03-17 19:56:50 +04:00
repoPath := RepoPath ( user . Name , repo . Name )
2014-03-11 14:10:19 +04:00
2014-03-17 19:56:50 +04:00
// Create bare new repository.
if err := extractGitBareZip ( repoPath ) ; err != nil {
return err
}
// Initialize repository according to user's choice.
fileName := map [ string ] string { }
2014-03-11 14:10:19 +04:00
if initReadme {
fileName [ "readme" ] = "README.md"
}
if repoLang != "" {
fileName [ "gitign" ] = ".gitignore"
2014-03-11 08:53:53 +04:00
}
2014-03-11 14:10:19 +04:00
if license != "" {
fileName [ "license" ] = "LICENSE"
}
2014-03-17 19:56:50 +04:00
// Clone to temprory path and do the init commit.
tmpDir := filepath . Join ( os . TempDir ( ) , fmt . Sprintf ( "%d" , time . Now ( ) . Nanosecond ( ) ) )
os . MkdirAll ( tmpDir , os . ModePerm )
2014-03-11 08:18:44 +04:00
2014-03-17 19:56:50 +04:00
if _ , _ , err := com . ExecCmd ( "git" , "clone" , repoPath , tmpDir ) ; err != nil {
return err
}
2014-03-11 08:18:44 +04:00
// README
2014-03-11 14:10:19 +04:00
if initReadme {
defaultReadme := repo . Name + "\n" + strings . Repeat ( "=" ,
utf8 . RuneCountInString ( repo . Name ) ) + "\n\n" + repo . Description
2014-03-17 19:56:50 +04:00
if err := ioutil . WriteFile ( filepath . Join ( tmpDir , fileName [ "readme" ] ) ,
2014-03-11 14:10:19 +04:00
[ ] byte ( defaultReadme ) , 0644 ) ; err != nil {
return err
}
2014-03-11 08:18:44 +04:00
}
2014-03-11 04:48:58 +04:00
2014-03-17 19:56:50 +04:00
// .gitignore
2014-03-11 14:10:19 +04:00
if repoLang != "" {
filePath := "conf/gitignore/" + repoLang
if com . IsFile ( filePath ) {
if _ , err := com . Copy ( filePath ,
2014-03-17 19:56:50 +04:00
filepath . Join ( tmpDir , fileName [ "gitign" ] ) ) ; err != nil {
2014-03-11 14:10:19 +04:00
return err
}
2014-03-11 09:32:36 +04:00
}
}
2014-03-11 08:53:53 +04:00
2014-03-17 19:56:50 +04:00
// LICENSE
2014-03-11 14:10:19 +04:00
if license != "" {
filePath := "conf/license/" + license
if com . IsFile ( filePath ) {
if _ , err := com . Copy ( filePath ,
2014-03-17 19:56:50 +04:00
filepath . Join ( tmpDir , fileName [ "license" ] ) ) ; err != nil {
2014-03-11 14:10:19 +04:00
return err
}
2014-03-11 09:32:36 +04:00
}
}
2014-03-11 08:18:44 +04:00
2014-03-17 19:56:50 +04:00
// Apply changes and commit.
if err := initRepoCommit ( tmpDir , user . NewGitSig ( ) ) ; err != nil {
2014-03-16 18:32:33 +04:00
return err
}
2014-03-17 19:56:50 +04:00
return nil
2014-03-11 04:48:58 +04:00
}
2014-03-13 06:27:11 +04:00
func GetRepositoryByName ( user * User , repoName string ) ( * Repository , error ) {
repo := & Repository {
OwnerId : user . Id ,
LowerName : strings . ToLower ( repoName ) ,
}
has , err := orm . Get ( repo )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
return repo , err
}
func GetRepositoryById ( id int64 ) ( repo * Repository , err error ) {
has , err := orm . Id ( id ) . Get ( repo )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
return repo , err
}
2014-02-19 22:04:31 +04:00
// GetRepositories returns the list of repositories of given user.
2014-03-10 04:06:29 +04:00
func GetRepositories ( user * User ) ( [ ] Repository , error ) {
repos := make ( [ ] Repository , 0 , 10 )
2014-03-16 10:28:24 +04:00
err := orm . Desc ( "updated" ) . Find ( & repos , & Repository { OwnerId : user . Id } )
2014-02-14 18:20:57 +04:00
return repos , err
}
2014-03-11 10:17:05 +04:00
func GetRepositoryCount ( user * User ) ( int64 , error ) {
return orm . Count ( & Repository { OwnerId : user . Id } )
}
2014-02-14 18:20:57 +04:00
func StarReposiory ( user * User , repoName string ) error {
return nil
}
func UnStarRepository ( ) {
}
func WatchRepository ( ) {
}
func UnWatchRepository ( ) {
}
2014-03-02 17:58:20 +04:00
func ForkRepository ( reposName string , userId int64 ) {
}
2014-02-20 10:53:56 +04:00
func RepoPath ( userName , repoName string ) string {
return filepath . Join ( UserPath ( userName ) , repoName + ".git" )
}
2014-02-15 03:16:54 +04:00
// DeleteRepository deletes a repository for a user or orgnaztion.
2014-03-13 10:39:09 +04:00
func DeleteRepository ( userId , repoId int64 , userName string ) ( err error ) {
repo := & Repository { Id : repoId , OwnerId : userId }
has , err := orm . Get ( repo )
if err != nil {
return err
} else if ! has {
return ErrRepoNotExist
}
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
2014-03-17 10:36:28 +04:00
if err = session . Begin ( ) ; err != nil {
return err
}
2014-03-13 10:39:09 +04:00
if _ , err = session . Delete ( & Repository { Id : repoId } ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-13 11:39:18 +04:00
if _ , err := session . Delete ( & Access { UserName : userName , RepoName : repo . Name } ) ; err != nil {
session . Rollback ( )
return err
}
2014-03-13 10:39:09 +04:00
if _ , err = session . Exec ( "update user set num_repos = num_repos - 1 where id = ?" , userId ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-02-15 03:16:54 +04:00
if err = session . Commit ( ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-03-13 10:39:09 +04:00
if err = os . RemoveAll ( RepoPath ( userName , repo . Name ) ) ; err != nil {
2014-02-14 18:20:57 +04:00
// TODO: log and delete manully
2014-03-13 10:39:09 +04:00
log . Error ( "delete repo %s/%s failed" , userName , repo . Name )
2014-02-14 18:20:57 +04:00
return err
}
return nil
}