Browse Source

#1487 Readme Template

tags/v0.9.99
Unknwon 8 years ago
parent
commit
bb3b90fcd6

+ 9
- 0
config.codekit View File

"strictMath": 0, "strictMath": 0,
"strictUnits": 0 "strictUnits": 0
}, },
"\/public\/ng\/css\/font-awesome.min.css": {
"fileType": 16,
"ignore": 0,
"ignoreWasSetByUser": 0,
"inputAbbreviatedPath": "\/public\/ng\/css\/font-awesome.min.css",
"outputAbbreviatedPath": "No Output Path",
"outputPathIsOutsideProject": 0,
"outputPathIsSetByUser": 0
},
"\/public\/ng\/css\/gogs.css": { "\/public\/ng\/css\/gogs.css": {
"fileType": 16, "fileType": 16,
"ignore": 1, "ignore": 1,

+ 110
- 82
models/repo.go View File

package models package models


import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"


// MigrateRepository migrates a existing repository from other project hosting. // MigrateRepository migrates a existing repository from other project hosting.
func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) { func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
repo, err := CreateRepository(u, CreateRepoOptions{
Name: name,
Description: desc,
IsPrivate: private,
IsMirror: mirror,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
[]byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"", setting.CustomConf)), 0777) []byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"", setting.CustomConf)), 0777)
} }


type CreateRepoOptions struct {
Name string
Description string
Gitignores string
License string
Readme string
IsPrivate bool
IsMirror bool
AutoInit bool
}

func getRepoInitFile(tp, name string) ([]byte, error) {
relPath := path.Join("conf", tp, name)

// Use custom file when available.
customPath := path.Join(setting.CustomPath, relPath)
if com.IsFile(customPath) {
return ioutil.ReadFile(customPath)
}
return bindata.Asset(relPath)
}

func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
// Clone to temprory path and do the init commit.
_, stderr, err := process.Exec(
fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir)
if err != nil {
return fmt.Errorf("git clone: %v - %s", err, stderr)
}

// README
data, err := getRepoInitFile("readme", opts.Readme)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", opts.Readme, err)
}

match := map[string]string{
"Name": repo.Name,
"Description": repo.Description,
}
if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"),
[]byte(com.Expand(string(data), match)), 0644); err != nil {
return fmt.Errorf("write README.md: %v", err)
}

// .gitignore
if len(opts.Gitignores) > 0 {
var buf bytes.Buffer
names := strings.Split(opts.Gitignores, ",")
for _, name := range names {
data, err = getRepoInitFile("gitignore", name)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", name, err)
}
buf.WriteString("# ---> " + name + "\n")
buf.Write(data)
buf.WriteString("\n")
}

if buf.Len() > 0 {
if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil {
return fmt.Errorf("write .gitignore: %v", err)
}
}
}

// LICENSE
if len(opts.License) > 0 {
data, err = getRepoInitFile("license", opts.License)
if err != nil {
return fmt.Errorf("getRepoInitFile[%s]: %v", opts.License, err)
}

if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil {
return fmt.Errorf("write LICENSE: %v", err)
}
}

return nil
}

// InitRepository initializes README and .gitignore if needed. // InitRepository initializes README and .gitignore if needed.
func initRepository(e Engine, repoPath string, u *User, repo *Repository, initReadme bool, repoLang, license string) error {
func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) error {
// Somehow the directory could exist. // Somehow the directory could exist.
if com.IsExist(repoPath) { if com.IsExist(repoPath) {
return fmt.Errorf("initRepository: path already exists: %s", repoPath) return fmt.Errorf("initRepository: path already exists: %s", repoPath)
// Init bare new repository. // Init bare new repository.
os.MkdirAll(repoPath, os.ModePerm) os.MkdirAll(repoPath, os.ModePerm)
_, stderr, err := process.ExecDir(-1, repoPath, _, stderr, err := process.ExecDir(-1, repoPath,
fmt.Sprintf("initRepository(git init --bare): %s", repoPath),
"git", "init", "--bare")
fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare")
if err != nil { if err != nil {
return fmt.Errorf("git init --bare: %s", err)
return fmt.Errorf("git init --bare: %v - %s", err, stderr)
} }


if err := createUpdateHook(repoPath); err != nil { if err := createUpdateHook(repoPath); err != nil {
return err return err
} }


// Initialize repository according to user's choice.
fileName := map[string]string{}
if initReadme {
fileName["readme"] = "README.md"
}
if repoLang != "" {
fileName["gitign"] = ".gitignore"
}
if license != "" {
fileName["license"] = "LICENSE"
}

// Clone to temprory path and do the init commit.
tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond()))
os.MkdirAll(tmpDir, os.ModePerm)
defer os.RemoveAll(tmpDir)


_, stderr, err = process.Exec(
fmt.Sprintf("initRepository(git clone): %s", repoPath),
"git", "clone", repoPath, tmpDir)
if err != nil {
return errors.New("git clone: " + stderr)
}
// Initialize repository according to user's choice.
if opts.AutoInit {
os.MkdirAll(tmpDir, os.ModePerm)
defer os.RemoveAll(tmpDir)


// README
if initReadme {
defaultReadme := repo.Name + "\n" + strings.Repeat("=",
utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description
if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]),
[]byte(defaultReadme), 0644); err != nil {
return err
if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %v", err)
} }
}


// FIXME: following two can be merged.

// .gitignore
// Copy custom file when available.
customPath := path.Join(setting.CustomPath, "conf/gitignore", repoLang)
targetPath := path.Join(tmpDir, fileName["gitign"])
if com.IsFile(customPath) {
if err := com.Copy(customPath, targetPath); err != nil {
return fmt.Errorf("copy gitignore: %v", err)
}
} else if com.IsSliceContainsStr(Gitignores, repoLang) {
if err = ioutil.WriteFile(targetPath,
bindata.MustAsset(path.Join("conf/gitignore", repoLang)), 0644); err != nil {
return fmt.Errorf("generate gitignore: %v", err)
// Apply changes and commit.
if err = initRepoCommit(tmpDir, u.NewGitSig()); err != nil {
return fmt.Errorf("initRepoCommit: %v", err)
} }
} else { } else {
delete(fileName, "gitign")
}

// LICENSE
customPath = path.Join(setting.CustomPath, "conf/license", license)
targetPath = path.Join(tmpDir, fileName["license"])
if com.IsFile(customPath) {
if err = com.Copy(customPath, targetPath); err != nil {
return fmt.Errorf("copy license: %v", err)
}
} else if com.IsSliceContainsStr(Licenses, license) {
if err = ioutil.WriteFile(targetPath,
bindata.MustAsset(path.Join("conf/license", license)), 0644); err != nil {
return fmt.Errorf("generate license: %v", err)
}
} else {
delete(fileName, "license")
repo.IsBare = true
} }


// Re-fetch the repository from database before updating it (else it would // Re-fetch the repository from database before updating it (else it would
if repo, err = getRepositoryByID(e, repo.ID); err != nil { if repo, err = getRepositoryByID(e, repo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %v", err) return fmt.Errorf("getRepositoryByID: %v", err)
} }
if len(fileName) == 0 {
repo.IsBare = true
}

repo.DefaultBranch = "master" repo.DefaultBranch = "master"
if err = updateRepository(e, repo, false); err != nil { if err = updateRepository(e, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err) return fmt.Errorf("updateRepository: %v", err)
} }


// Ignore init process if user choose not to.
if len(fileName) == 0 {
return nil
}

// Apply changes and commit.
return initRepoCommit(tmpDir, u.NewGitSig())
return nil
} }


func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
} }


// CreateRepository creates a repository for given user or organization. // CreateRepository creates a repository for given user or organization.
func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (_ *Repository, err error) {
func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) {
repo := &Repository{ repo := &Repository{
OwnerID: u.Id, OwnerID: u.Id,
Owner: u, Owner: u,
Name: name,
LowerName: strings.ToLower(name),
Description: desc,
IsPrivate: isPrivate,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
IsPrivate: opts.IsPrivate,
} }


sess := x.NewSession() sess := x.NewSession()
} }


// No need for init mirror. // No need for init mirror.
if !isMirror {
if !opts.IsMirror {
repoPath := RepoPath(u.Name, repo.Name) repoPath := RepoPath(u.Name, repo.Name)
if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil {
if err = initRepository(sess, repoPath, u, repo, opts); err != nil {
if err2 := os.RemoveAll(repoPath); err2 != nil { if err2 := os.RemoveAll(repoPath); err2 != nil {
log.Error(4, "initRepository: %v", err) log.Error(4, "initRepository: %v", err)
return nil, fmt.Errorf( return nil, fmt.Errorf(

+ 6
- 0
modules/auth/auth.go View File

return getSize(field, "MaxSize(") return getSize(field, "MaxSize(")
} }


// FIXME: struct contains a struct
func validateStruct(obj interface{}) binding.Errors {

return nil
}

func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors { func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors {
if errs.Len() == 0 { if errs.Len() == 0 {
return errs return errs

+ 10
- 11
modules/auth/repo_form.go View File

// |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______| // |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______|
// \/ \/ \/ \/ \/ \/ \/ // \/ \/ \/ \/ \/ \/ \/


type RepoForm struct {
type CreateRepoForm struct {
Uid int64 `binding:"Required"` Uid int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool Private bool
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(255)"`
}

type CreateRepoForm struct {
RepoForm
AutoInit bool
Gitignores string
License string
Readme string
AutoInit bool
Gitignores string
License string
Readme string
} }


func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
CloneAddr string `binding:"Required"` CloneAddr string `binding:"Required"`
AuthUsername string AuthUsername string
AuthPassword string AuthPassword string
RepoForm
Mirror bool
Mirror bool
Uid int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool
Description string `binding:"MaxSize(255)"`
} }


func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 4
- 0
public/ng/css/font-awesome.min.css
File diff suppressed because it is too large
View File


+ 1
- 1
public/ng/css/gogs.css View File

font-size: 16px; font-size: 16px;
line-height: 1.6; line-height: 1.6;
word-wrap: break-word; word-wrap: break-word;
padding: 0 2em 2em !important;
padding: 5px 2em 2em !important;
} }
.markdown > *:first-child { .markdown > *:first-child {
margin-top: 0 !important; margin-top: 0 !important;

BIN
public/ng/fonts/FontAwesome.otf View File


BIN
public/ng/fonts/fontawesome-webfont.eot View File


+ 601
- 465
public/ng/fonts/fontawesome-webfont.svg
File diff suppressed because it is too large
View File


BIN
public/ng/fonts/fontawesome-webfont.ttf View File


BIN
public/ng/fonts/fontawesome-webfont.woff View File


BIN
public/ng/fonts/fontawesome-webfont.woff2 View File


+ 1
- 1
public/ng/less/gogs/markdown.less View File

font-size:16px; font-size:16px;
line-height:1.6; line-height:1.6;
word-wrap:break-word; word-wrap:break-word;
padding: 0 2em 2em !important;
padding: 5px 2em 2em !important;


>*:first-child { >*:first-child {
margin-top:0 !important; margin-top:0 !important;

+ 9
- 2
routers/api/v1/repo.go View File

} }


func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) { func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) {
repo, err := models.CreateRepository(owner, opt.Name, opt.Description,
opt.Gitignore, opt.License, opt.Private, false, opt.AutoInit)
repo, err := models.CreateRepository(owner, models.CreateRepoOptions{
Name: opt.Name,
Description: opt.Description,
Gitignores: opt.Gitignore,
License: opt.License,
// Readme: form.Readme,
IsPrivate: opt.Private,
AutoInit: opt.AutoInit,
})
if err != nil { if err != nil {
if models.IsErrRepoAlreadyExist(err) || if models.IsErrRepoAlreadyExist(err) ||
models.IsErrNameReserved(err) || models.IsErrNameReserved(err) ||

+ 37
- 69
routers/repo/repo.go View File

) )


func checkContextUser(ctx *middleware.Context, uid int64) *models.User { func checkContextUser(ctx *middleware.Context, uid int64) *models.User {
if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "GetOrganizations", err)
return nil
}
ctx.Data["Orgs"] = ctx.User.Orgs

// Not equal means current user is an organization. // Not equal means current user is an organization.
if uid == ctx.User.Id || uid == 0 { if uid == ctx.User.Id || uid == 0 {
return ctx.User return ctx.User
if err != nil { if err != nil {
ctx.Handle(500, "checkContextUser", fmt.Errorf("GetUserById(%d): %v", uid, err)) ctx.Handle(500, "checkContextUser", fmt.Errorf("GetUserById(%d): %v", uid, err))
return nil return nil
} else if !org.IsOrganization() {
}

// Check ownership of organization.
if !org.IsOrganization() || !org.IsOwnedBy(ctx.User.Id) {
ctx.Error(403) ctx.Error(403)
return nil return nil
} }
} }
ctx.Data["ContextUser"] = ctxUser ctx.Data["ContextUser"] = ctxUser


if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "GetOrganizations", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs

ctx.HTML(200, CREATE) ctx.HTML(200, CREATE)
} }


func handleCreateError(ctx *middleware.Context, err error, name string, tpl base.TplName, form interface{}) {
switch {
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
case models.IsErrNameReserved(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
case models.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
default:
ctx.Handle(500, name, err)
}
}

func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
ctx.Data["Title"] = ctx.Tr("new_repo") ctx.Data["Title"] = ctx.Tr("new_repo")


} }
ctx.Data["ContextUser"] = ctxUser ctx.Data["ContextUser"] = ctxUser


if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "GetOrganizations", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs

if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, CREATE) ctx.HTML(200, CREATE)
return return
} }


if ctxUser.IsOrganization() {
// Check ownership of organization.
if !ctxUser.IsOwnedBy(ctx.User.Id) {
ctx.Error(403)
return
}
}

repo, err := models.CreateRepository(ctxUser, form.RepoName, form.Description,
form.Gitignores, form.License, form.Private, false, form.AutoInit)
repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{
Name: form.RepoName,
Description: form.Description,
Gitignores: form.Gitignores,
License: form.License,
Readme: form.Readme,
IsPrivate: form.Private,
AutoInit: form.AutoInit,
})
if err == nil { if err == nil {
log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name) log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name) ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
} }
} }


switch {
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), CREATE, &form)
case models.IsErrNameReserved(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), CREATE, &form)
case models.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), CREATE, &form)
default:
ctx.Handle(500, "CreatePost", err)
}
handleCreateError(ctx, err, "CreatePost", CREATE, &form)
} }


func Migrate(ctx *middleware.Context) { func Migrate(ctx *middleware.Context) {
} }
ctx.Data["ContextUser"] = ctxUser ctx.Data["ContextUser"] = ctxUser


if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "GetOrganizations", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs

ctx.HTML(200, MIGRATE) ctx.HTML(200, MIGRATE)
} }


} }
ctx.Data["ContextUser"] = ctxUser ctx.Data["ContextUser"] = ctxUser


if err := ctx.User.GetOrganizations(); err != nil {
ctx.Handle(500, "GetOrganizations", err)
return
}
ctx.Data["Orgs"] = ctx.User.Orgs

if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, MIGRATE) ctx.HTML(200, MIGRATE)
return return
} }


if ctxUser.IsOrganization() {
// Check ownership of organization.
if !ctxUser.IsOwnedBy(ctx.User.Id) {
ctx.Error(403)
return
}
}

// Remote address can be HTTP/HTTPS/Git URL or local path. // Remote address can be HTTP/HTTPS/Git URL or local path.
// Note: remember to change api/v1/repo.go: MigrateRepo // Note: remember to change api/v1/repo.go: MigrateRepo
// FIXME: merge these two functions with better error handling // FIXME: merge these two functions with better error handling
return return
} }


switch {
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), MIGRATE, &form)
case models.IsErrNameReserved(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), MIGRATE, &form)
case models.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), MIGRATE, &form)
default:
ctx.Handle(500, "MigratePost", err)
}
handleCreateError(ctx, err, "MigratePost", MIGRATE, &form)
} }


func Action(ctx *middleware.Context) { func Action(ctx *middleware.Context) {

+ 1
- 1
templates/ng/base/head.tmpl View File

<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" /> <link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />




<link rel="stylesheet" href="{{AppSubUrl}}/css/font-awesome.min.css">
<link rel="stylesheet" href="{{AppSubUrl}}/ng/css/font-awesome.min.css">
<script src="{{AppSubUrl}}/ng/js/lib/jquery-1.11.1.min.js"></script> <script src="{{AppSubUrl}}/ng/js/lib/jquery-1.11.1.min.js"></script>



+ 2
- 2
templates/repo/create.tmpl View File

<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.repo_lang"}}</label> <label>{{.i18n.Tr "repo.repo_lang"}}</label>
<div class="ui multiple search normal selection dropdown"> <div class="ui multiple search normal selection dropdown">
<input type="hidden" name="gitignores" value="{{.gitignoresValue}}">
<input type="hidden" name="gitignores" value="{{.gitignores}}">
<div class="default text">{{.i18n.Tr "repo.repo_lang_helper"}}</div> <div class="default text">{{.i18n.Tr "repo.repo_lang_helper"}}</div>
<div class="menu"> <div class="menu">
{{range .Gitignores}} {{range .Gitignores}}
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.readme"}}</label> <label>{{.i18n.Tr "repo.readme"}}</label>
<div class="ui selection dropdown"> <div class="ui selection dropdown">
<input type="hidden" name="license" value="{{.readme}}">
<input type="hidden" name="readme" value="{{.readme}}">
<div class="default text">{{.i18n.Tr "repo.readme_helper"}}</div> <div class="default text">{{.i18n.Tr "repo.readme_helper"}}</div>
<div class="menu"> <div class="menu">
{{range .Readmes}} {{range .Readmes}}

Loading…
Cancel
Save