Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
gravityblast committed Jan 15, 2014
1 parent c390709 commit c89a2c3
Show file tree
Hide file tree
Showing 17 changed files with 727 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)

.DS_Store
tmp
/fresh
/runner.conf.dev

*.o
*.a
*.so
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2014 Andrea Franz
Copyright (c) 2014 Andrea Franz (http://gravityblast.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
Expand Down
31 changes: 31 additions & 0 deletions _examples/gocraft-web/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"
"github.com/gocraft/web"
"github.com/pilu/fresh/runner/runnerutils"
"net/http"
)

func runnerMiddleware(rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) {
if runnerutils.HasErrors() {
runnerutils.RenderError(rw)
return
}

next(rw, req)
}

type Context struct{}

func (c *Context) SayHello(rw web.ResponseWriter, req *web.Request) {
fmt.Fprint(rw, "Hello World")
}

func main() {
router := web.New(Context{}).
Middleware(web.LoggerMiddleware).
Middleware(runnerMiddleware).
Get("/", (*Context).SayHello)
http.ListenAndServe("localhost:3000", router)
}
27 changes: 27 additions & 0 deletions _examples/martini/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"github.com/codegangsta/martini"
"github.com/pilu/fresh/runner/runnerutils"
"net/http"
"os"
)

func runnerMiddleware(w http.ResponseWriter, r *http.Request) {
if runnerutils.HasErrors() {
runnerutils.RenderError(w)
}
}

func main() {
m := martini.Classic()

if os.Getenv("DEV_RUNNER") == "1" {
m.Use(runnerMiddleware)
}

m.Get("/", func() string {
return "Hello world - Martini"
})
m.Run()
}
13 changes: 13 additions & 0 deletions _examples/pilu-martini/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import (
"github.com/pilu/martini"
)

func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world - Martini"
})
m.Run()
}
15 changes: 15 additions & 0 deletions _examples/traffic/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"github.com/pilu/traffic"
)

func rootHandler(w traffic.ResponseWriter, r *traffic.Request) {
w.WriteText("Hello World - Traffic")
}

func main() {
router := traffic.New()
router.Get("/", rootHandler)
router.Run()
}
34 changes: 34 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Fresh is a command line tool that builds and (re)starts your web application everytime you save a go or template file.
If the web framework you are using supports Fresh, it will show build errors on your browser.
It currently works with Traffic (https://github.com/pilu/traffic), Martini (https://github.com/codegangsta/martini) and gocraft/web (https://github.com/gocraft/web).
When you start the `fresh` from your web app folder, Fresh builds the application and (re)starts it. It does the same every time a file is created/modified.
If the build fails, it logs the error message in a file in the tmp folder.
*/
package main

import (
"flag"
"fmt"
"github.com/pilu/fresh/runner"
"os"
)

func main() {
configPath := flag.String("c", "", "config file path")
flag.Parse()

if *configPath != "" {
if _, err := os.Stat(*configPath); err != nil {
fmt.Printf("Can't find config file `%s`\n", *configPath)
os.Exit(1)
} else {
os.Setenv("RUNNER_CONFIG_PATH", *configPath)
}
}

runner.Start()
}
12 changes: 12 additions & 0 deletions runner.conf.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root: .
tmp_path: ./tmp
build_name: runner-build
build_log: runner-build-errors.log
valid_ext: .go, .tpl, .tmpl, .html
build_delay: 600
colors: 1
log_color_main: cyan
log_color_build: yellow
log_color_runner: green
log_color_watcher: magenta
log_color_app:
39 changes: 39 additions & 0 deletions runner/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package runner

import (
"io"
"io/ioutil"
"os"
"os/exec"
)

func build() (string, bool) {
buildLog("Building...")

cmd := exec.Command("go", "build", "-o", buildPath(), root())

stderr, err := cmd.StderrPipe()
if err != nil {
fatal(err)
}

stdout, err := cmd.StdoutPipe()
if err != nil {
fatal(err)
}

err = cmd.Start()
if err != nil {
fatal(err)
}

io.Copy(os.Stdout, stdout)
errBuf, _ := ioutil.ReadAll(stderr)

err = cmd.Wait()
if err != nil {
return string(errBuf), false
}

return "", true
}
40 changes: 40 additions & 0 deletions runner/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package runner

import (
"fmt"
logPkg "log"
"os"
"time"
)

type logFunc func(string, ...interface{})

var logger = logPkg.New(os.Stderr, "", 0)

func newLogFunc(prefix string) func(string, ...interface{}) {
color, clear := "", ""
if settings["colors"] == "1" {
color = fmt.Sprintf("\033[%sm", logColor(prefix))
clear = fmt.Sprintf("\033[%sm", colors["reset"])
}
prefix = fmt.Sprintf("%-11s", prefix)

return func(format string, v ...interface{}) {
now := time.Now()
timeString := fmt.Sprintf("%d:%d:%02d", now.Hour(), now.Minute(), now.Second())
format = fmt.Sprintf("%s%s %s |%s %s", color, timeString, prefix, clear, format)
logger.Printf(format, v...)
}
}

func fatal(err error) {
logger.Fatal(err)
}

type appLogWriter struct{}

func (a appLogWriter) Write(p []byte) (n int, err error) {
appLog(string(p))

return len(p), nil
}
41 changes: 41 additions & 0 deletions runner/runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package runner

import (
"io"
"os/exec"
)

func run() bool {
runnerLog("Running...")

cmd := exec.Command(buildPath())

stderr, err := cmd.StderrPipe()
if err != nil {
fatal(err)
}

stdout, err := cmd.StdoutPipe()
if err != nil {
fatal(err)
}

err = cmd.Start()
if err != nil {
fatal(err)
}

go func() {
io.Copy(appLogWriter{}, stderr)
io.Copy(appLogWriter{}, stdout)
}()

go func() {
<-stopChannel
pid := cmd.Process.Pid
runnerLog("Killing PID %d", pid)
cmd.Process.Kill()
}()

return true
}
80 changes: 80 additions & 0 deletions runner/runnerutils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package runnerutils

import (
"bufio"
"html/template"
"io/ioutil"
"net/http"
"os"
"path/filepath"
)

var logFilePath string

func init() {
root := os.Getenv("RUNNER_WD")
tmpPath := os.Getenv("RUNNER_TMP_PATH")
fileName := os.Getenv("RUNNER_BUILD_LOG")
logFilePath = filepath.Join(root, tmpPath, fileName)
}

// Returns true if a build error file exists in the tmp folder.
func HasErrors() bool {
if _, err := os.Stat(logFilePath); err == nil {
return true
}

return false
}

// It renders an error page with the build error message.
func RenderError(w http.ResponseWriter) {
data := map[string]interface{}{
"Output": readErrorFile(),
}

w.Header().Set("Content-Type", "text/html")
tpl := template.Must(template.New("ErrorPage").Parse(buildPageTpl))
tpl.Execute(w, data)
}

func readErrorFile() string {
file, err := os.Open(logFilePath)
if err != nil {
return ""
}

defer file.Close()

reader := bufio.NewReader(file)
bytes, _ := ioutil.ReadAll(reader)

return string(bytes)
}

const buildPageTpl string = `
<html>
<head>
<title>Traffic Panic</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
html, body{ padding: 0; margin: 0; }
header { background: #C52F24; color: white; border-bottom: 2px solid #9C0606; }
h1 { padding: 10px 0; margin: 0; }
.container { margin: 0 20px; }
.output { height: 300px; overflow-y: scroll; border: 1px solid #e5e5e5; padding: 10px; }
</style>
</head>
<body>
<header>
<div class="container">
<h1>Build Error</h1>
</div>
</header>
<div class="container">
<pre class="output">{{ .Output }}</pre>
</div>
</body>
</html>
`
Loading

0 comments on commit c89a2c3

Please sign in to comment.