func InitSetting() { var err error WorkDir, err = os.Getwd() if err != nil { log.Fatal("Fail to get work directory: %v", err) } confPath := path.Join(WorkDir, ".bra.toml") if !com.IsFile(confPath) { log.Fatal(".bra.toml not found in work directory") } else if _, err = toml.DecodeFile(confPath, &Cfg); err != nil { log.Fatal("Fail to decode .bra.toml: %v", err) } if Cfg.Run.InterruptTimeout == 0 { Cfg.Run.InterruptTimeout = 15 } // Init default ignore lists. Cfg.Run.IgnoreDirs = com.AppendStr(Cfg.Run.IgnoreDirs, ".git") Cfg.Run.IgnoreRegexps = make([]*regexp.Regexp, len(Cfg.Run.IgnoreFiles)) for i, regStr := range Cfg.Run.IgnoreFiles { Cfg.Run.IgnoreRegexps[i], err = regexp.Compile(regStr) if err != nil { log.Fatal("Invalid regexp[%s]: %v", regStr, err) } } }
func runInit(ctx *cli.Context) { if com.IsExist(".bra.toml") { fmt.Print("There is a .bra.toml in the work directory, do you want to overwrite?(y/n): ") var answer string fmt.Scan(&answer) if strings.ToLower(answer) != "y" { fmt.Println("Existed file is untouched.") return } } wd, err := os.Getwd() if err != nil { log.Fatal("Fail to get work directory: %v", err) } data, err := bindata.Asset("templates/default.bra.toml") if err != nil { log.Fatal("Fail to get asset: %v", err) } appName := filepath.Base(wd) if runtime.GOOS == "windows" { appName += ".exe" } data = bytes.Replace(data, []byte("$APP_NAME"), []byte(appName), -1) if err := ioutil.WriteFile(".bra.toml", data, os.ModePerm); err != nil { log.Fatal("Fail to generate default .bra.toml: %v", err) } }
func NewContext() { if com.IsExist(HTMLRoot) { if err := os.RemoveAll(HTMLRoot); err != nil { log.Fatal("Fail to clean up HTMLRoot: %v", err) } } if err := ReloadDocs(); err != nil { log.Fatal("Fail to init docs: %v", err) } }
func init() { var err error os.MkdirAll(path.Dir(setting.DatabasePath), os.ModePerm) x, err = xorm.NewEngine("sqlite3", "file:"+setting.DatabasePath+"?cache=shared&mode=rwc") if err != nil { log.Fatal("Fail to init database: %v", err) } x.SetLogger(nil) if err = x.Sync2(new(Event)); err != nil { log.Fatal("Fail to sync database: %v", err) } }
func InitSetting() { var err error WorkDir, err = os.Getwd() if err != nil { log.Fatal("Fail to get work directory: %v", err) } confPath := path.Join(WorkDir, ".bra.toml") if !com.IsFile(confPath) { log.Fatal(".bra.toml not found in work directory") } else if _, err = toml.DecodeFile(confPath, &Cfg); err != nil { log.Fatal("Fail to decode .bra.toml: %v", err) } }
func runClient(ctx *cli.Context) { if len(setting.Cfg.Sync.RemoteAddr) == 0 { log.Fatal("Remote address cannot be empty") } watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal("Fail to create new watcher: %v", err) } defer watcher.Close() go func() { for { select { case e := <-watcher.Events: if isTmpFile(e.Name) { continue } if e.Op&fsnotify.Create == fsnotify.Create || e.Op&fsnotify.Write == fsnotify.Write { if com.IsDir(e.Name) { log.Warn("Hasn't support directory yet") continue } sendFile(strings.TrimPrefix(e.Name, setting.WorkDir+"/")) } } } }() if err = watcher.Add(setting.WorkDir); err != nil { log.Fatal("Fail to watch directory(%s): %v", setting.WorkDir, err) } dirs, err := com.GetAllSubDirs(setting.WorkDir) if err != nil { log.Fatal("Fail to get subdirectories(%s): %v", setting.WorkDir, err) } for _, dir := range dirs { if err = watcher.Add(path.Join(setting.WorkDir, dir)); err != nil { log.Fatal("Fail to watch directory(%s): %v", path.Join(setting.WorkDir, dir), err) } } log.Info("Start watching...") select {} }
func main() { log.Info("Peach %s", APP_VER) m := macaron.New() m.Use(macaron.Logger()) m.Use(macaron.Recovery()) m.Use(macaron.Statics(macaron.StaticOptions{ SkipLogging: setting.ProdMode, }, "custom/public", "public")) m.Use(i18n.I18n(i18n.Options{ Files: setting.Docs.Locales, })) tplDir := "templates" if setting.Page.UseCustomTpl { tplDir = "custom/templates" } m.Use(pongo2.Pongoer(pongo2.Options{ Directory: tplDir, })) m.Use(middleware.Contexter()) m.Get("/", routers.Home) m.Get("/docs", routers.Docs) m.Get("/docs/images/*", routers.DocsStatic) m.Get("/docs/*", routers.Docs) m.Post("/hook", routers.Hook) m.Get("/search", routers.Search) m.Get("/*", routers.Pages) m.NotFound(routers.NotFound) listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HTTPPort) log.Info("%s Listen on %s", setting.Site.Name, listenAddr) log.Fatal("Fail to start Peach: %v", http.ListenAndServe(listenAddr, m)) }
func runSync(ctx *cli.Context) { setup(ctx) switch ctx.String("mode") { case "server": runServer(ctx) case "client": runClient(ctx) default: log.Fatal("Unrecognized run mode: %s", ctx.String("mode")) } }
func runServer(ctx *cli.Context) { if len(setting.Cfg.Sync.ListenAddr) == 0 { log.Fatal("Listen address cannot be empty") } l, err := net.Listen("tcp", setting.Cfg.Sync.ListenAddr) if err != nil { log.Fatal("Fail to start server on listening(%s): %v", setting.Cfg.Sync.ListenAddr, err) } log.Info("Listening on %s...", setting.Cfg.Sync.ListenAddr) for { conn, err := l.Accept() if err != nil { if ne, ok := err.(net.Error); !ok || !ne.Temporary() { log.Warn("Network error when accpet: %v", err) } continue } go receiveHandler(conn) } }
func runWeb(ctx *cli.Context) { if ctx.IsSet("config") { setting.CustomConf = ctx.String("config") } setting.NewContext() models.NewContext() log.Info("Peach %s", setting.AppVer) m := macaron.New() m.Use(macaron.Logger()) m.Use(macaron.Recovery()) m.Use(macaron.Statics(macaron.StaticOptions{ SkipLogging: setting.ProdMode, }, "custom/public", "public", models.HTMLRoot)) m.Use(i18n.I18n(i18n.Options{ Files: setting.Docs.Locales, DefaultLang: setting.Docs.Langs[0], })) tplDir := "templates" if setting.Page.UseCustomTpl { tplDir = "custom/templates" } m.Use(pongo2.Pongoer(pongo2.Options{ Directory: tplDir, })) m.Use(middleware.Contexter()) m.Get("/", routers.Home) m.Get("/docs", routers.Docs) m.Get("/docs/images/*", routers.DocsStatic) m.Get("/docs/*", routers.Protect, routers.Docs) m.Post("/hook", routers.Hook) m.Get("/search", routers.Search) m.Get("/*", routers.Pages) m.NotFound(routers.NotFound) listenAddr := fmt.Sprintf("0.0.0.0:%d", setting.HTTPPort) log.Info("%s Listen on %s", setting.Site.Name, listenAddr) log.Fatal("Fail to start Peach: %v", http.ListenAndServe(listenAddr, m)) }
func runRun(ctx *cli.Context) { setup(ctx) go catchSignals() go notify(setting.Cfg.Run.InitCmds) watchPathes := append([]string{setting.WorkDir}, setting.Cfg.Run.WatchDirs...) if setting.Cfg.Run.WatchAll { subdirs := make([]string, 0, 10) for _, dir := range watchPathes[1:] { dirs, err := com.GetAllSubDirs(setting.UnpackPath(dir)) if err != nil { log.Fatal("Fail to get sub-directories: %v", err) } for i := range dirs { if !setting.IgnoreDir(dirs[i]) { subdirs = append(subdirs, path.Join(dir, dirs[i])) } } } watchPathes = append(watchPathes, subdirs...) } watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal("Fail to create new watcher: %v", err) } defer watcher.Close() go func() { for { select { case e := <-watcher.Events: needsNotify := true if isTmpFile(e.Name) || !hasWatchExt(e.Name) || setting.IgnoreFile(e.Name) { continue } // Prevent duplicated builds. if lastBuild.Add(time.Duration(setting.Cfg.Run.BuildDelay) * time.Millisecond). After(time.Now()) { continue } lastBuild = time.Now() showName := e.String() if !log.NonColor { showName = strings.Replace(showName, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1) } if e.Op&fsnotify.Remove != fsnotify.Remove { mt, err := com.FileMTime(e.Name) if err != nil { log.Error("Fail to get file modify time: %v", err) continue } if eventTime[e.Name] == mt { log.Debug("Skipped %s", showName) needsNotify = false } eventTime[e.Name] = mt } if needsNotify { log.Info(showName) if runningCmd != nil && runningCmd.Process != nil { if runningCmd.Args[0] == "sudo" && runtime.GOOS == "linux" { // 给父进程发送一个TERM信号,试图杀死它和它的子进程 rootCmd := exec.Command("sudo", "kill", "-TERM", com.ToStr(runningCmd.Process.Pid)) rootCmd.Stdout = os.Stdout rootCmd.Stderr = os.Stderr if err := rootCmd.Run(); err != nil { log.Error("Fail to start rootCmd %s", err.Error()) fmt.Print("\x07") } } else { shutdown <- true } } go notify(setting.Cfg.Run.Cmds) } } } }() log.Info("Following directories are monitored:") for i, p := range watchPathes { if err = watcher.Add(setting.UnpackPath(p)); err != nil { log.Fatal("Fail to watch diretory(%s): %v", p, err) } if i > 0 && !log.NonColor { p = strings.Replace(p, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1) p = strings.Replace(p, "$WORKDIR", "\033[47;30m$WORKDIR\033[0m", 1) } fmt.Printf("-> %s\n", p) } select {} }
func NewContext() { log.Prefix = "[Peach]" if !com.IsFile(CustomConf) { log.Fatal("No custom configuration found: 'custom/app.ini'") } sources := []interface{}{"conf/app.ini", CustomConf} var err error Cfg, err = macaron.SetConfig(sources[0], sources[1:]...) if err != nil { log.Fatal("Fail to load config: %v", err) } sec := Cfg.Section("") if sec.Key("RUN_MODE").String() == "prod" { ProdMode = true macaron.Env = macaron.PROD macaron.ColorLog = false } HTTPPort = sec.Key("HTTP_PORT").MustInt(5555) sec = Cfg.Section("site") Site.Name = sec.Key("NAME").MustString("Peach Server") Site.Desc = sec.Key("DESC").String() Site.UseCDN = sec.Key("USE_CDN").MustBool() Site.URL = sec.Key("URL").String() sec = Cfg.Section("page") Page.HasLandingPage = sec.Key("HAS_LANDING_PAGE").MustBool() Page.DocsBaseURL = sec.Key("DOCS_BASE_URL").Validate(func(in string) string { if len(in) == 0 { return "/docs" } else if in[0] != '/' { return "/" + in } return in }) Page.UseCustomTpl = sec.Key("USE_CUSTOM_TPL").MustBool() Page.NavbarTplPath = "navbar.html" Page.HomeTplPath = "home.html" Page.DocsTplPath = "docs.html" Page.FooterTplPath = "footer.html" Page.DisqusTplPath = "disqus.html" sec = Cfg.Section("navbar") list := sec.KeyStrings() Navbar.Items = make([]*NavbarItem, len(list)) for i, name := range list { secName := "navbar." + sec.Key(name).String() Navbar.Items[i] = &NavbarItem{ Icon: Cfg.Section(secName).Key("ICON").String(), Locale: Cfg.Section(secName).Key("LOCALE").MustString(secName), Link: Cfg.Section(secName).Key("LINK").MustString("/"), Blank: Cfg.Section(secName).Key("BLANK").MustBool(), } } sec = Cfg.Section("asset") Asset.CustomCSS = sec.Key("CUSTOM_CSS").String() sec = Cfg.Section("docs") Docs.Type = sec.Key("TYPE").In("local", []string{"local", "remote"}) Docs.Target = sec.Key("TARGET").String() Docs.Secret = sec.Key("SECRET").String() Docs.Langs = Cfg.Section("i18n").Key("LANGS").Strings(",") Docs.Locales = make(map[string][]byte) for _, lang := range Docs.Langs { if lang != "en-US" && lang != "zh-CN" { Docs.Locales["locale_"+lang+".ini"] = []byte("") } } sec = Cfg.Section("extension") Extension.EnableDisqus = sec.Key("ENABLE_DISQUS").MustBool() Extension.DisqusShortName = sec.Key("DISQUS_SHORT_NAME").String() Extension.HighlightJSCustomCSS = sec.Key("HIGHLIGHTJS_CUSTOM_CSS").String() Extension.EnableSearch = sec.Key("ENABLE_SEARCH").MustBool() Extension.GABlock = sec.Key("GA_BLOCK").String() }
func init() { log.Prefix = "[CGX]" var err error Cfg, err = ini.Load("conf/app.ini") if err != nil { log.Fatal("Fail to load config: %v", err) } if com.IsFile("custom/app.ini") { if err = Cfg.Append("custom/app.ini"); err != nil { log.Fatal("Fail to load custom config: %v", err) } } AppName = Cfg.Section("").Key("APP_NAME").MustString("Continuous Go Cross-compiler") RunMode = Cfg.Section("").Key("RUN_MODE").In("dev", []string{"dev", "prod"}) if RunMode == "prod" { macaron.Env = macaron.PROD } HTTPPort = Cfg.Section("").Key("HTTP_PORT").MustInt(3050) DatabasePath = Cfg.Section("").Key("DATABASE_PATH").MustString("data/cgx.db") ArchivePath = Cfg.Section("").Key("ARCHIVE_PATH").MustString("data/archive") if !filepath.IsAbs(GOPATH) { wd, _ := os.Getwd() GOPATH = filepath.Join(wd, GOPATH) } sec := Cfg.Section("repository") Repository.ImportPath = sec.Key("IMPORT_PATH").MustString("") sec = Cfg.Section("webhook") Webhook.Mode = sec.Key("MODE").In("test", []string{"test", "travis", "github"}) branchesInfo := Cfg.Section("branches").Keys() Branches = make([]string, len(branchesInfo)) for i := range branchesInfo { Branches[i] = branchesInfo[i].String() } targetsInfo := Cfg.Section("targets").Keys() Targets = make([]Target, len(targetsInfo)) for i := range targetsInfo { infos := targetsInfo[i].Strings(" ") if len(infos) < 2 { log.Fatal("target at least contain GOOS and GOARCH: %s", targetsInfo[i]) } Targets[i].GOOS = infos[0] Targets[i].GOARCH = infos[1] if len(infos) >= 3 { Targets[i].GOARM = infos[2] } } resInfo := Cfg.Section("resources").Keys() Resources = make([]string, len(resInfo)) for i := range resInfo { Resources[i] = resInfo[i].MustString("") } }
func NewContext() { if err := ReloadDocs(); err != nil { log.Fatal("Fail to init docs: %v", err) } }