diff --git a/.travis.yml b/.travis.yml index b060c693..ae853369 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,11 @@ language: go go: - 1.2 - 1.3 + - 1.4 + - tip sudo: false + +notifications: + email: + - u@gogs.io \ No newline at end of file diff --git a/cmd/web.go b/cmd/web.go index 241abf2c..e6fb2925 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -53,7 +53,9 @@ var CmdWeb = cli.Command{ Description: `Gogs web server is the only thing you need to run, and it takes care of all the other things for you`, Action: runWeb, - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + cli.StringFlag{"port, p", "3000", "Temporary port number to prevent conflict", ""}, + }, } type VerChecker struct { @@ -162,7 +164,7 @@ func newMacaron() *macaron.Macaron { return m } -func runWeb(*cli.Context) { +func runWeb(ctx *cli.Context) { routers.GlobalInit() checkVersion() @@ -179,9 +181,9 @@ func runWeb(*cli.Context) { // Routers. m.Get("/", ignSignIn, routers.Home) m.Get("/explore", ignSignIn, routers.Explore) - // FIXME: when i'm binding form here??? - m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) - m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) + m.Combo("/install", routers.InstallInit). + Get(routers.Install). + Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) m.Group("", func() { m.Get("/pulls", user.Pulls) m.Get("/issues", user.Issues) @@ -460,6 +462,12 @@ func runWeb(*cli.Context) { // Not found handler. m.NotFound(routers.NotFound) + // Flag for port number in case first time run conflict. + if ctx.IsSet("port") { + setting.AppUrl = strings.Replace(setting.AppUrl, setting.HttpPort, ctx.String("port"), 1) + setting.HttpPort = ctx.String("port") + } + var err error listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort) log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl) diff --git a/conf/app.ini b/conf/app.ini index 1af480a8..07242150 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -1,3 +1,6 @@ +# NEVER EVER MODIFY THIS FILE +# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE + ; App name that shows on every page title APP_NAME = Gogs: Go Git Service ; Change it if you run locally diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 7db7ca0d..534e8d38 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -59,6 +59,8 @@ run_user = Run User run_user_helper = The user must have access to Repository Root Path and run Gogs. domain = Domain domain_helper = This affects SSH clone URLs. +http_port = HTTP Port +http_port_helper = Port number which application will listen on. app_url = Application URL app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail. email_title = E-mail Service Settings (Optional) diff --git a/models/models.go b/models/models.go index 55e7bf58..df030e51 100644 --- a/models/models.go +++ b/models/models.go @@ -32,7 +32,7 @@ var ( HasEngine bool DbCfg struct { - Type, Host, Name, User, Pwd, Path, SslMode string + Type, Host, Name, User, Passwd, Path, SSLMode string } EnableSQLite3 bool @@ -58,10 +58,10 @@ func LoadModelsConfig() { DbCfg.Host = sec.Key("HOST").String() DbCfg.Name = sec.Key("NAME").String() DbCfg.User = sec.Key("USER").String() - if len(DbCfg.Pwd) == 0 { - DbCfg.Pwd = sec.Key("PASSWD").String() + if len(DbCfg.Passwd) == 0 { + DbCfg.Passwd = sec.Key("PASSWD").String() } - DbCfg.SslMode = sec.Key("SSL_MODE").String() + DbCfg.SSLMode = sec.Key("SSL_MODE").String() DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db") } @@ -70,7 +70,7 @@ func getEngine() (*xorm.Engine, error) { switch DbCfg.Type { case "mysql": cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", - DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name) + DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name) case "postgres": var host, port = "127.0.0.1", "5432" fields := strings.Split(DbCfg.Host, ":") @@ -81,7 +81,7 @@ func getEngine() (*xorm.Engine, error) { port = fields[1] } cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s", - DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode) + DbCfg.User, DbCfg.Passwd, host, port, DbCfg.Name, DbCfg.SSLMode) case "sqlite3": if !EnableSQLite3 { return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type) @@ -97,7 +97,7 @@ func getEngine() (*xorm.Engine, error) { func NewTestEngine(x *xorm.Engine) (err error) { x, err = getEngine() if err != nil { - return fmt.Errorf("models.init(fail to connect to database): %v", err) + return fmt.Errorf("connect to database: %v", err) } return x.Sync(tables...) @@ -106,7 +106,7 @@ func NewTestEngine(x *xorm.Engine) (err error) { func SetEngine() (err error) { x, err = getEngine() if err != nil { - return fmt.Errorf("models.init(fail to connect to database): %v", err) + return fmt.Errorf("connect to database: %v", err) } // WARNING: for serv command, MUST remove the output to os.stdout, diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 1dd96d8d..ad7ce5b9 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -9,6 +9,7 @@ import ( "reflect" "strings" + "github.com/Unknwon/com" "github.com/Unknwon/macaron" "github.com/macaron-contrib/binding" "github.com/macaron-contrib/session" @@ -135,6 +136,10 @@ type Form interface { binding.Validator } +func init() { + binding.SetNameMapper(com.ToSnakeCase) +} + // AssignForm assign form values back to the template data. func AssignForm(form interface{}, data map[string]interface{}) { typ := reflect.TypeOf(form) @@ -152,6 +157,8 @@ func AssignForm(form interface{}, data map[string]interface{}) { // Allow ignored fields in the struct if fieldName == "-" { continue + } else if len(fieldName) == 0 { + fieldName = com.ToSnakeCase(field.Name) } data[fieldName] = val.Field(i).Interface() diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index becd5cbc..3c0ff651 100644 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -12,26 +12,27 @@ import ( ) type InstallForm struct { - Database string `form:"database" binding:"Required"` - DbHost string `form:"host"` - DbUser string `form:"user"` - DbPasswd string `form:"passwd"` - DatabaseName string `form:"database_name"` - SslMode string `form:"ssl_mode"` - DatabasePath string `form:"database_path"` - RepoRootPath string `form:"repo_path" binding:"Required"` - RunUser string `form:"run_user" binding:"Required"` - Domain string `form:"domain" binding:"Required"` - AppUrl string `form:"app_url" binding:"Required"` - SmtpHost string `form:"smtp_host"` - SmtpEmail string `form:"mailer_user"` - SmtpPasswd string `form:"mailer_pwd"` - RegisterConfirm string `form:"register_confirm"` - MailNotify string `form:"mail_notify"` - AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` - AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(255)"` - ConfirmPasswd string `form:"confirm_passwd" binding:"Required;MinSize(6);MaxSize(255)"` - AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` + DbType string `binding:"Required"` + DbHost string + DbUser string + DbPasswd string + DbName string + SSLMode string + DbPath string + RepoRootPath string `binding:"Required"` + RunUser string `binding:"Required"` + Domain string `binding:"Required"` + HTTPPort string `binding:"Required"` + AppUrl string `binding:"Required"` + SMTPHost string + SMTPEmail string + SMTPPasswd string + RegisterConfirm string + MailNotify string + AdminName string `binding:"Required;AlphaDashDot;MaxSize(30)"` + AdminPasswd string `binding:"Required;MinSize(6);MaxSize(255)"` + AdminConfirmPasswd string `binding:"Required;MinSize(6);MaxSize(255)"` + AdminEmail string `binding:"Required;Email;MaxSize(50)"` } func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index bc9da3c6..e7c44cdd 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -178,7 +178,7 @@ func NewConfigContext() { log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err) } } else { - log.Warn("No custom 'conf/app.ini' found, please go to '/install'") + log.Warn("No custom 'conf/app.ini' found, ignore this if you're running first time") } Cfg.NameMapper = ini.AllCapsUnderscore diff --git a/routers/install.go b/routers/install.go index a2491c4f..58e6cf66 100644 --- a/routers/install.go +++ b/routers/install.go @@ -73,12 +73,7 @@ func GlobalInit() { checkRunMode() } -func renderDbOption(ctx *middleware.Context) { - ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"} -} - -// @router /install [get] -func Install(ctx *middleware.Context, form auth.InstallForm) { +func InstallInit(ctx *middleware.Context) { if setting.InstallLock { ctx.Handle(404, "Install", errors.New("Installation is prohibited")) return @@ -87,46 +82,35 @@ func Install(ctx *middleware.Context, form auth.InstallForm) { ctx.Data["Title"] = ctx.Tr("install.install") ctx.Data["PageIsInstall"] = true - // FIXME: when i'm ckeching length here? should they all be 0 no matter when? - // Get and assign values to install form. - if len(form.DbHost) == 0 { - form.DbHost = models.DbCfg.Host - } - if len(form.DbUser) == 0 { - form.DbUser = models.DbCfg.User - } - if len(form.DbPasswd) == 0 { - form.DbPasswd = models.DbCfg.Pwd - } - if len(form.DatabaseName) == 0 { - form.DatabaseName = models.DbCfg.Name - } - if len(form.DatabasePath) == 0 { - form.DatabasePath = models.DbCfg.Path - } + ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"} +} - if len(form.RepoRootPath) == 0 { - form.RepoRootPath = setting.RepoRootPath - } - if len(form.RunUser) == 0 { - // Note: it's not normall to use SSH in windows so current user can be first option(not git). - if setting.IsWindows && setting.RunUser == "git" { - form.RunUser = os.Getenv("USER") - if len(form.RunUser) == 0 { - form.RunUser = os.Getenv("USERNAME") - } - } else { - form.RunUser = setting.RunUser +func Install(ctx *middleware.Context) { + form := auth.InstallForm{} + + form.DbHost = models.DbCfg.Host + form.DbUser = models.DbCfg.User + form.DbPasswd = models.DbCfg.Passwd + form.DbName = models.DbCfg.Name + form.DbPath = models.DbCfg.Path + + form.RepoRootPath = setting.RepoRootPath + + // Note(unknwon): it's hard for Windows users change a running user, + // so just use current one if config says default. + if setting.IsWindows && setting.RunUser == "git" { + form.RunUser = os.Getenv("USER") + if len(form.RunUser) == 0 { + form.RunUser = os.Getenv("USERNAME") } - } - if len(form.Domain) == 0 { - form.Domain = setting.Domain - } - if len(form.AppUrl) == 0 { - form.AppUrl = setting.AppUrl + } else { + form.RunUser = setting.RunUser } - renderDbOption(ctx) + form.Domain = setting.Domain + form.HTTPPort = setting.HttpPort + form.AppUrl = setting.AppUrl + curDbOp := "" if models.EnableSQLite3 { curDbOp = "SQLite3" // Default when enabled. @@ -138,16 +122,7 @@ func Install(ctx *middleware.Context, form auth.InstallForm) { } func InstallPost(ctx *middleware.Context, form auth.InstallForm) { - if setting.InstallLock { - ctx.Handle(404, "InstallPost", errors.New("Installation is prohibited")) - return - } - - ctx.Data["Title"] = ctx.Tr("install.install") - ctx.Data["PageIsInstall"] = true - - renderDbOption(ctx) - ctx.Data["CurDbOption"] = form.Database + ctx.Data["CurDbOption"] = form.DbType if ctx.HasError() { ctx.HTML(200, INSTALL) @@ -162,18 +137,17 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { // Pass basic check, now test configuration. // Test database setting. dbTypes := map[string]string{"MySQL": "mysql", "PostgreSQL": "postgres", "SQLite3": "sqlite3"} - models.DbCfg.Type = dbTypes[form.Database] + models.DbCfg.Type = dbTypes[form.DbType] models.DbCfg.Host = form.DbHost models.DbCfg.User = form.DbUser - models.DbCfg.Pwd = form.DbPasswd - models.DbCfg.Name = form.DatabaseName - models.DbCfg.SslMode = form.SslMode - models.DbCfg.Path = form.DatabasePath + models.DbCfg.Passwd = form.DbPasswd + models.DbCfg.Name = form.DbName + models.DbCfg.SSLMode = form.SSLMode + models.DbCfg.Path = form.DbPath // Set test engine. var x *xorm.Engine if err := models.NewTestEngine(x); err != nil { - // FIXME: should use core.QueryDriver (github.com/go-xorm/core) if strings.Contains(err.Error(), `Unknown database type: sqlite3`) { ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "http://gogs.io/docs/installation/install_from_binary.html"), INSTALL, &form) } else { @@ -194,7 +168,6 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { if len(curUser) == 0 { curUser = os.Getenv("USERNAME") } - // Does not check run user when the install lock is off. if form.RunUser != curUser { ctx.Data["Err_RunUser"] = true ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, curUser), INSTALL, &form) @@ -202,31 +175,36 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { } // Check admin password. - if form.AdminPasswd != form.ConfirmPasswd { + if form.AdminPasswd != form.AdminConfirmPasswd { ctx.Data["Err_AdminPasswd"] = true ctx.RenderWithErr(ctx.Tr("form.password_not_match"), INSTALL, form) return } + if form.AppUrl[len(form.AppUrl)-1] != '/' { + form.AppUrl += "/" + } + // Save settings. setting.Cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type) setting.Cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host) setting.Cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name) setting.Cfg.Section("database").Key("USER").SetValue(models.DbCfg.User) - setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Pwd) - setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SslMode) + setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Passwd) + setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SSLMode) setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path) setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath) setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser) setting.Cfg.Section("server").Key("DOMAIN").SetValue(form.Domain) + setting.Cfg.Section("server").Key("HTTP_PORT").SetValue(form.HTTPPort) setting.Cfg.Section("server").Key("ROOT_URL").SetValue(form.AppUrl) - if len(strings.TrimSpace(form.SmtpHost)) > 0 { + if len(strings.TrimSpace(form.SMTPHost)) > 0 { setting.Cfg.Section("mailer").Key("ENABLED").SetValue("true") - setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SmtpHost) - setting.Cfg.Section("mailer").Key("USER").SetValue(form.SmtpEmail) - setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SmtpPasswd) + setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost) + setting.Cfg.Section("mailer").Key("USER").SetValue(form.SMTPEmail) + setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) setting.Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on")) setting.Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "on")) @@ -264,5 +242,5 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { log.Info("First-time run install finished!") ctx.Flash.Success(ctx.Tr("install.install_success")) - ctx.Redirect(setting.AppSubUrl + "/user/login") + ctx.Redirect(form.AppUrl + "user/login") } diff --git a/templates/install.tmpl b/templates/install.tmpl index f1c28031..3a7eb787 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -13,7 +13,7 @@