Merging develop to master for v0.13.0 release

This commit is contained in:
Jeevanandam M
2016-06-05 18:32:53 -07:00
14 changed files with 170 additions and 64 deletions

View File

@@ -80,6 +80,7 @@ func Build(buildFlags ...string) (app *App, compileError *revel.Error) {
versionLinkerFlags := fmt.Sprintf("-X %s/app.APP_VERSION \"%s\"", revel.ImportPath, appVersion) versionLinkerFlags := fmt.Sprintf("-X %s/app.APP_VERSION \"%s\"", revel.ImportPath, appVersion)
flags := []string{ flags := []string{
"build", "build",
"-i",
"-ldflags", versionLinkerFlags, "-ldflags", versionLinkerFlags,
"-tags", buildTags, "-tags", buildTags,
"-o", binName} "-o", binName}
@@ -173,12 +174,16 @@ func cleanDir(dir string) {
tmpPath := path.Join(revel.AppPath, dir) tmpPath := path.Join(revel.AppPath, dir)
f, err := os.Open(tmpPath) f, err := os.Open(tmpPath)
if err != nil { if err != nil {
revel.ERROR.Println("Failed to clean dir:", err) if !os.IsNotExist(err) {
revel.ERROR.Println("Failed to clean dir:", err)
}
} else { } else {
defer f.Close() defer f.Close()
infos, err := f.Readdir(0) infos, err := f.Readdir(0)
if err != nil { if err != nil {
revel.ERROR.Println("Failed to clean dir:", err) if !os.IsNotExist(err) {
revel.ERROR.Println("Failed to clean dir:", err)
}
} else { } else {
for _, info := range infos { for _, info := range infos {
path := path.Join(tmpPath, info.Name()) path := path.Join(tmpPath, info.Name())
@@ -198,7 +203,6 @@ func cleanDir(dir string) {
} }
} }
// genSource renders the given template to produce source code, which it writes // genSource renders the given template to produce source code, which it writes
// to the given directory and file. // to the given directory and file.
func genSource(dir, filename, templateSource string, args map[string]interface{}) { func genSource(dir, filename, templateSource string, args map[string]interface{}) {

View File

@@ -13,7 +13,6 @@ package harness
import ( import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"github.com/revel/revel"
"go/build" "go/build"
"io" "io"
"net" "net"
@@ -22,10 +21,11 @@ import (
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync/atomic" "sync/atomic"
"github.com/revel/revel"
) )
var ( var (
@@ -52,7 +52,7 @@ func renderError(w http.ResponseWriter, r *http.Request, err error) {
// ServeHTTP handles all requests. // ServeHTTP handles all requests.
// It checks for changes to app, rebuilds if necessary, and forwards the request. // It checks for changes to app, rebuilds if necessary, and forwards the request.
func (hp *Harness) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *Harness) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Don't rebuild the app for favicon requests. // Don't rebuild the app for favicon requests.
if lastRequestHadError > 0 && r.URL.Path == "/favicon.ico" { if lastRequestHadError > 0 && r.URL.Path == "/favicon.ico" {
return return
@@ -71,18 +71,19 @@ func (hp *Harness) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Reverse proxy the request. // Reverse proxy the request.
// (Need special code for websockets, courtesy of bradfitz) // (Need special code for websockets, courtesy of bradfitz)
if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") { if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") {
proxyWebsocket(w, r, hp.serverHost) proxyWebsocket(w, r, h.serverHost)
} else { } else {
hp.proxy.ServeHTTP(w, r) h.proxy.ServeHTTP(w, r)
} }
} }
// Return a reverse proxy that forwards requests to the given port. // NewHarness method returns a reverse proxy that forwards requests
// to the given port.
func NewHarness() *Harness { func NewHarness() *Harness {
// Get a template loader to render errors. // Get a template loader to render errors.
// Prefer the app's views/errors directory, and fall back to the stock error pages. // Prefer the app's views/errors directory, and fall back to the stock error pages.
revel.MainTemplateLoader = revel.NewTemplateLoader( revel.MainTemplateLoader = revel.NewTemplateLoader(
[]string{path.Join(revel.RevelPath, "templates")}) []string{filepath.Join(revel.RevelPath, "templates")})
revel.MainTemplateLoader.Refresh() revel.MainTemplateLoader.Refresh()
addr := revel.HttpAddr addr := revel.HttpAddr
@@ -101,12 +102,12 @@ func NewHarness() *Harness {
port = getFreePort() port = getFreePort()
} }
serverUrl, _ := url.ParseRequestURI(fmt.Sprintf(scheme+"://%s:%d", addr, port)) serverURL, _ := url.ParseRequestURI(fmt.Sprintf(scheme+"://%s:%d", addr, port))
harness := &Harness{ harness := &Harness{
port: port, port: port,
serverHost: serverUrl.String()[len(scheme+"://"):], serverHost: serverURL.String()[len(scheme+"://"):],
proxy: httputil.NewSingleHostReverseProxy(serverUrl), proxy: httputil.NewSingleHostReverseProxy(serverURL),
} }
if revel.HttpSsl { if revel.HttpSsl {
@@ -117,7 +118,7 @@ func NewHarness() *Harness {
return harness return harness
} }
// Rebuild the Revel application and run it on the given port. // Refresh method rebuilds the Revel application and run it on the given port.
func (h *Harness) Refresh() (err *revel.Error) { func (h *Harness) Refresh() (err *revel.Error) {
if h.app != nil { if h.app != nil {
h.app.Kill() h.app.Kill()
@@ -140,10 +141,14 @@ func (h *Harness) Refresh() (err *revel.Error) {
return return
} }
// WatchDir method returns false to file matches with doNotWatch
// otheriwse true
func (h *Harness) WatchDir(info os.FileInfo) bool { func (h *Harness) WatchDir(info os.FileInfo) bool {
return !revel.ContainsString(doNotWatch, info.Name()) return !revel.ContainsString(doNotWatch, info.Name())
} }
// WatchFile method returns true given filename HasSuffix of ".go"
// otheriwse false
func (h *Harness) WatchFile(filename string) bool { func (h *Harness) WatchFile(filename string) bool {
return strings.HasSuffix(filename, ".go") return strings.HasSuffix(filename, ".go")
} }
@@ -204,7 +209,18 @@ func getFreePort() (port int) {
// proxyWebsocket copies data between websocket client and server until one side // proxyWebsocket copies data between websocket client and server until one side
// closes the connection. (ReverseProxy doesn't work with websocket requests.) // closes the connection. (ReverseProxy doesn't work with websocket requests.)
func proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) { func proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) {
d, err := net.Dial("tcp", host) var (
d net.Conn
err error
)
if revel.HttpSsl {
// since this proxy isn't used in production,
// it's OK to set InsecureSkipVerify to true
// no need to add another configuration option.
d, err = tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true})
} else {
d, err = net.Dial("tcp", host)
}
if err != nil { if err != nil {
http.Error(w, "Error contacting backend server.", 500) http.Error(w, "Error contacting backend server.", 500)
revel.ERROR.Printf("Error dialing websocket backend %s: %v", host, err) revel.ERROR.Printf("Error dialing websocket backend %s: %v", host, err)

View File

@@ -96,7 +96,7 @@ func ProcessSource(roots []string) (*SourceInfo, *revel.Error) {
} }
// Start walking the directory tree. // Start walking the directory tree.
filepath.Walk(root, func(path string, info os.FileInfo, err error) error { _ = revel.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
log.Println("Error scanning app source:", err) log.Println("Error scanning app source:", err)
return nil return nil
@@ -433,7 +433,8 @@ func appendAction(fset *token.FileSet, mm methodMap, decl ast.Decl, pkgImportPat
var importPath string var importPath string
typeExpr := NewTypeExpr(pkgName, field.Type) typeExpr := NewTypeExpr(pkgName, field.Type)
if !typeExpr.Valid { if !typeExpr.Valid {
return // We didn't understand one of the args. Ignore this action. (Already logged) log.Printf("Didn't understand argument '%s' of action %s. Ignoring.\n", name, getFuncName(funcDecl))
return // We didn't understand one of the args. Ignore this action.
} }
if typeExpr.PkgName != "" { if typeExpr.PkgName != "" {
var ok bool var ok bool

View File

@@ -7,17 +7,22 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/revel/revel"
"github.com/revel/cmd/harness" "github.com/revel/cmd/harness"
"github.com/revel/revel"
) )
var cmdBuild = &Command{ var cmdBuild = &Command{
UsageLine: "build [import path] [target path]", UsageLine: "build [import path] [target path] [run mode]",
Short: "build a Revel application (e.g. for deployment)", Short: "build a Revel application (e.g. for deployment)",
Long: ` Long: `
Build the Revel web application named by the given import path. Build the Revel web application named by the given import path.
This allows it to be deployed and run on a machine that lacks a Go installation. This allows it to be deployed and run on a machine that lacks a Go installation.
The run mode is used to select which set of app.conf configuration should
apply and may be used to determine logic in the application itself.
Run mode defaults to "dev".
WARNING: The target path will be completely deleted, if it already exists! WARNING: The target path will be completely deleted, if it already exists!
For example: For example:
@@ -31,14 +36,18 @@ func init() {
} }
func buildApp(args []string) { func buildApp(args []string) {
if len(args) != 2 { if len(args) < 2 {
fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long) fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long)
return return
} }
appImportPath, destPath := args[0], args[1] appImportPath, destPath, mode := args[0], args[1], "dev"
if len(args) >= 3 {
mode = args[2]
}
if !revel.Initialized { if !revel.Initialized {
revel.Init("", appImportPath, "") revel.Init(mode, appImportPath, "")
} }
// First, verify that it is either already empty or looks like a previous // First, verify that it is either already empty or looks like a previous
@@ -96,6 +105,7 @@ func buildApp(args []string) {
tmplData, runShPath := map[string]interface{}{ tmplData, runShPath := map[string]interface{}{
"BinName": filepath.Base(app.BinaryPath), "BinName": filepath.Base(app.BinaryPath),
"ImportPath": appImportPath, "ImportPath": appImportPath,
"Mode": mode,
}, path.Join(destPath, "run.sh") }, path.Join(destPath, "run.sh")
mustRenderTemplate( mustRenderTemplate(

View File

@@ -17,7 +17,7 @@ For example:
revel clean github.com/revel/samples/chat revel clean github.com/revel/samples/chat
It removes the app/tmp directory. It removes the app/tmp and app/routes directory.
`, `,
} }
@@ -37,12 +37,17 @@ func cleanApp(args []string) {
return return
} }
// Remove the app/tmp directory. purgeDirs := []string{
tmpDir := path.Join(appPkg.Dir, "app", "tmp") path.Join(appPkg.Dir, "app", "tmp"),
fmt.Println("Removing:", tmpDir) path.Join(appPkg.Dir, "app", "routes"),
err = os.RemoveAll(tmpDir) }
if err != nil {
fmt.Fprintln(os.Stderr, "Abort:", err) for _, dir := range purgeDirs {
return fmt.Println("Removing:", dir)
err = os.RemoveAll(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "Abort:", err)
return
}
} }
} }

View File

@@ -4,10 +4,12 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/build" "go/build"
"log"
"math/rand" "math/rand"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"github.com/revel/revel" "github.com/revel/revel"
) )
@@ -60,6 +62,8 @@ func newApp(args []string) {
errorf("Too many arguments provided.\nRun 'revel help new' for usage.\n") errorf("Too many arguments provided.\nRun 'revel help new' for usage.\n")
} }
revel.ERROR.SetFlags(log.LstdFlags)
// checking and setting go paths // checking and setting go paths
initGoPaths() initGoPaths()
@@ -96,9 +100,6 @@ func initGoPaths() {
"Please refer to http://golang.org/doc/code.html to configure your Go environment.") "Please refer to http://golang.org/doc/code.html to configure your Go environment.")
} }
// set go src path
srcRoot = filepath.Join(filepath.SplitList(gopath)[0], "src")
// check for go executable // check for go executable
var err error var err error
gocmd, err = exec.LookPath("go") gocmd, err = exec.LookPath("go")
@@ -106,12 +107,38 @@ func initGoPaths() {
errorf("Go executable not found in PATH.") errorf("Go executable not found in PATH.")
} }
// revel/revel#1004 choose go path relative to current working directory
workingDir, _ := os.Getwd()
goPathList := filepath.SplitList(gopath)
for _, path := range goPathList {
if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
srcRoot = path
break
}
path, _ = filepath.EvalSymlinks(path)
if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
srcRoot = path
break
}
}
if len(srcRoot) == 0 {
revel.ERROR.Fatalln("Abort: could not create a Revel application outside of GOPATH.")
}
// set go src path
srcRoot = filepath.Join(srcRoot, "src")
} }
func setApplicationPath(args []string) { func setApplicationPath(args []string) {
var err error var err error
importPath = args[0] importPath = args[0]
if filepath.IsAbs(importPath) {
// revel/revel#1014 validate relative path, we cannot use built-in functions
// since Go import path is valid relative path too.
// so check basic part of the path, which is "."
if filepath.IsAbs(importPath) || strings.HasPrefix(importPath, ".") {
errorf("Abort: '%s' looks like a directory. Please provide a Go import path instead.", errorf("Abort: '%s' looks like a directory. Please provide a Go import path instead.",
importPath) importPath)
} }

View File

@@ -2,19 +2,25 @@ package main
import ( import (
"fmt" "fmt"
"github.com/revel/revel"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"github.com/revel/revel"
) )
var cmdPackage = &Command{ var cmdPackage = &Command{
UsageLine: "package [import path]", UsageLine: "package [import path] [run mode]",
Short: "package a Revel application (e.g. for deployment)", Short: "package a Revel application (e.g. for deployment)",
Long: ` Long: `
Package the Revel web application named by the given import path. Package the Revel web application named by the given import path.
This allows it to be deployed and run on a machine that lacks a Go installation. This allows it to be deployed and run on a machine that lacks a Go installation.
The run mode is used to select which set of app.conf configuration should
apply and may be used to determine logic in the application itself.
Run mode defaults to "dev".
For example: For example:
revel package github.com/revel/samples/chat revel package github.com/revel/samples/chat
@@ -31,8 +37,14 @@ func packageApp(args []string) {
return return
} }
// Determine the run mode.
mode := "dev"
if len(args) >= 2 {
mode = args[1]
}
appImportPath := args[0] appImportPath := args[0]
revel.Init("", appImportPath, "") revel.Init(mode, appImportPath, "")
// Remove the archive if it already exists. // Remove the archive if it already exists.
destFile := filepath.Base(revel.BasePath) + ".tar.gz" destFile := filepath.Base(revel.BasePath) + ".tar.gz"
@@ -42,7 +54,7 @@ func packageApp(args []string) {
tmpDir, err := ioutil.TempDir("", filepath.Base(revel.BasePath)) tmpDir, err := ioutil.TempDir("", filepath.Base(revel.BasePath))
panicOnError(err, "Failed to get temp dir") panicOnError(err, "Failed to get temp dir")
buildApp([]string{args[0], tmpDir}) buildApp([]string{args[0], tmpDir, mode})
// Create the zip file. // Create the zip file.
archiveName := mustTarGzDir(destFile, tmpDir) archiveName := mustTarGzDir(destFile, tmpDir)

View File

@@ -1,2 +1,2 @@
@echo off @echo off
{{.BinName}} -importPath {{.ImportPath}} -srcPath %CD%\src -runMode prod {{.BinName}} -importPath {{.ImportPath}} -srcPath %CD%\src -runMode {{.Mode}}

View File

@@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd) SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode prod "$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}}

View File

@@ -4,7 +4,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/agtorre/gocolorize"
"io" "io"
"math/rand" "math/rand"
"os" "os"
@@ -12,9 +11,11 @@ import (
"strings" "strings"
"text/template" "text/template"
"time" "time"
"github.com/agtorre/gocolorize"
) )
// Cribbed from the genius organization of the "go" command. // Command structure cribbed from the genius organization of the "go" command.
type Command struct { type Command struct {
Run func(args []string) Run func(args []string)
UsageLine, Short, Long string UsageLine, Short, Long string
@@ -36,6 +37,7 @@ var commands = []*Command{
cmdPackage, cmdPackage,
cmdClean, cmdClean,
cmdTest, cmdTest,
cmdVersion,
} }
func main() { func main() {

View File

@@ -1,8 +1,8 @@
package main package main
import ( import (
"github.com/revel/revel"
"github.com/revel/cmd/harness" "github.com/revel/cmd/harness"
"github.com/revel/revel"
"strconv" "strconv"
) )
@@ -65,7 +65,7 @@ func runApp(args []string) {
} }
// Else, just build and run the app. // Else, just build and run the app.
revel.TRACE.Println("Running in live build mode.") revel.TRACE.Println("Running in live build mode.")
app, err := harness.Build() app, err := harness.Build()
if err != nil { if err != nil {
errorf("Failed to build app: %s", err) errorf("Failed to build app: %s", err)

View File

@@ -3,9 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/revel/revel"
"github.com/revel/cmd/harness"
"github.com/revel/modules/testrunner/app/controllers"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@@ -13,6 +10,10 @@ import (
"path" "path"
"strings" "strings"
"time" "time"
"github.com/revel/cmd/harness"
"github.com/revel/modules/testrunner/app/controllers"
"github.com/revel/revel"
) )
var cmdTest = &Command{ var cmdTest = &Command{
@@ -117,13 +118,19 @@ You can add it to a run mode configuration with the following line:
) )
for i := 0; ; i++ { for i := 0; ; i++ {
if resp, err = http.Get(baseUrl + "/@tests.list"); err == nil { if resp, err = http.Get(baseUrl + "/@tests.list"); err == nil {
break if resp.StatusCode == http.StatusOK {
break
}
} }
if i < 3 { if i < 3 {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
continue continue
} }
errorf("Failed to request test list: %s", err) if err != nil {
errorf("Failed to request test list: %s", err)
} else {
errorf("Failed to request test list: non-200 response")
}
} }
defer resp.Body.Close() defer resp.Body.Close()
json.NewDecoder(resp.Body).Decode(&testSuites) json.NewDecoder(resp.Body).Decode(&testSuites)

View File

@@ -66,22 +66,10 @@ func mustChmod(filename string, mode os.FileMode) {
// Additionally, the trailing ".template" is stripped from the file name. // Additionally, the trailing ".template" is stripped from the file name.
// Also, dot files and dot directories are skipped. // Also, dot files and dot directories are skipped.
func mustCopyDir(destDir, srcDir string, data map[string]interface{}) error { func mustCopyDir(destDir, srcDir string, data map[string]interface{}) error {
var fullSrcDir string return revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
// Handle symlinked directories.
f, err := os.Lstat(srcDir)
if err == nil && f.Mode()&os.ModeSymlink == os.ModeSymlink {
fullSrcDir, err = os.Readlink(srcDir)
if err != nil {
panic(err)
}
} else {
fullSrcDir = srcDir
}
return filepath.Walk(fullSrcDir, func(srcPath string, info os.FileInfo, err error) error {
// Get the relative path from the source base, and the corresponding path in // Get the relative path from the source base, and the corresponding path in
// the dest directory. // the dest directory.
relSrcPath := strings.TrimLeft(srcPath[len(fullSrcDir):], string(os.PathSeparator)) relSrcPath := strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator))
destPath := path.Join(destDir, relSrcPath) destPath := path.Join(destDir, relSrcPath)
// Skip dot files and dot directories. // Skip dot files and dot directories.
@@ -124,7 +112,7 @@ func mustTarGzDir(destFilename, srcDir string) string {
tarWriter := tar.NewWriter(gzipWriter) tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close() defer tarWriter.Close()
filepath.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error { revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
if info.IsDir() { if info.IsDir() {
return nil return nil
} }

34
revel/version.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"runtime"
"github.com/revel/revel"
)
var cmdVersion = &Command{
UsageLine: "version",
Short: "displays the Revel Framework and Go version",
Long: `
Displays the Revel Framework and Go version.
For example:
revel version
`,
}
func init() {
cmdVersion.Run = versionApp
}
func versionApp(args []string) {
fmt.Printf("Version(s):")
fmt.Printf("\n Revel v%v (%v)", revel.Version, revel.BuildDate)
fmt.Printf("\n %s %s/%s\n\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
}