├── LICENSE
├── README.md
├── cmd
└── lime
│ └── main.go
├── lime.png
├── main.go
├── server
└── server.go
├── vendor.conf
├── vendor
└── github.com
│ └── codegangsta
│ └── gin
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ ├── builder.go
│ ├── config.go
│ ├── proxy.go
│ └── runner.go
│ └── wercker.yml
└── wercker.yml
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Shintaro Kaneko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lime
2 |
3 | [](https://app.wercker.com/project/bykey/5ae8f488a3136a826b480a6bbf33138a)
4 |
5 | `lime` is a simple command line utility for live-reloading Go applications.
6 | Just run `lime` in your app directory and your web app will be served with
7 | `lime` as a proxy. `lime` will automatically recompile your code when it
8 | detects a change. Your app will be restarted the next time it receives an
9 | HTTP request.
10 |
11 | `lime` adheres to the "silence is golden" principle, so it will only complain
12 | if there was a compiler error or if you succesfully compile after an error.
13 |
14 | 
15 |
16 | ## Installation
17 |
18 | ```shell
19 | go get github.com/kaneshin/lime
20 | ```
21 |
22 | Make sure that `lime` was installed correctly:
23 |
24 | ```shell
25 | lime -h
26 | ```
27 |
28 | ## Usage
29 |
30 | ```shell
31 | cd /path/to/app
32 | lime
33 | ```
34 |
35 | ### Example
36 |
37 | ```shell
38 | lime -bin=/tmp/bin -ignore-pattern="(\\.git)" -path=./app -immediate=true ./app -version
39 | ```
40 |
41 | ### Options
42 |
43 | | option | description |
44 | | :----- | :---------- |
45 | | port | port for the proxy server |
46 | | app-port | port for the Go web server |
47 | | bin | locates built binary |
48 | | ignore-pattern | pattern to ignore |
49 | | build-pattern | pattern to build |
50 | | run-pattern | pattern to run |
51 | | path, -t "." | watch directory |
52 | | immediate, -i | run the server immediately after it's built |
53 | | godep, -g | use godep when building |
54 |
55 |
56 | ## License
57 |
58 | [The MIT License (MIT)](http://kaneshin.mit-license.org/)
59 |
60 |
61 | ## Author
62 |
63 | Shintaro Kaneko
64 |
--------------------------------------------------------------------------------
/cmd/lime/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 | "net/http/httptest"
7 | "regexp"
8 | "strings"
9 |
10 | "github.com/codegangsta/cli"
11 | "github.com/codegangsta/gin/lib"
12 |
13 | "log"
14 | "os"
15 | "os/exec"
16 | "os/signal"
17 | "path/filepath"
18 | "strconv"
19 | "syscall"
20 | "time"
21 | )
22 |
23 | var (
24 | buildTime = time.Now()
25 | logger = struct {
26 | info *log.Logger
27 | warn *log.Logger
28 | }{
29 | log.New(os.Stdout, "[lime-INFO] ", log.Ldate|log.Ltime),
30 | log.New(os.Stdout, "[lime-WARN] ", log.Ldate|log.Ltime),
31 | }
32 | immediate = false
33 | verbose = false
34 | )
35 |
36 | func main() {
37 | app := cli.NewApp()
38 | app.Name = "lime"
39 | app.Usage = "A live reload utility for Go applications."
40 | app.Version = "1.0.0"
41 | app.Action = mainAction
42 | app.Flags = []cli.Flag{
43 | cli.IntFlag{
44 | Name: "port",
45 | Usage: "port for the proxy server",
46 | },
47 | cli.IntFlag{
48 | Name: "app-port",
49 | Usage: "port for the Go web server",
50 | },
51 | cli.StringFlag{
52 | Name: "bin,b",
53 | Value: "./lime-bin",
54 | Usage: "path of generated binary file",
55 | },
56 | cli.StringFlag{
57 | Name: "ignore-pattern",
58 | Usage: "pattern to ignore",
59 | },
60 | cli.StringFlag{
61 | Name: "build-pattern",
62 | Value: "(\\.go)",
63 | Usage: "pattern to build",
64 | },
65 | cli.StringFlag{
66 | Name: "run-pattern",
67 | Value: "(\\.html|\\.css|\\.js)",
68 | Usage: "pattern to run",
69 | },
70 | cli.StringFlag{
71 | Name: "path,t",
72 | Value: ".",
73 | Usage: "path to watch files from",
74 | },
75 | cli.BoolFlag{
76 | Name: "immediate,i",
77 | Usage: "run the server immediately after it's built",
78 | },
79 | cli.BoolFlag{
80 | Name: "verbose",
81 | Usage: "show verbose output",
82 | },
83 | cli.BoolFlag{
84 | Name: "godep,g",
85 | Usage: "use godep when building",
86 | },
87 | }
88 |
89 | app.Run(os.Args)
90 | }
91 |
92 | var (
93 | ipat *regexp.Regexp
94 | bpat *regexp.Regexp
95 | rpat *regexp.Regexp
96 | )
97 |
98 | func mainAction(c *cli.Context) {
99 | // TODO: New feature to check update binary is
100 | // logger.info.Printf("Skipping lime update check.\n")
101 | immediate = c.GlobalBool("immediate")
102 | verbose = c.GlobalBool("verbose")
103 |
104 | wd, err := os.Getwd()
105 | if err != nil {
106 | logger.warn.Fatal(err)
107 | }
108 |
109 | bp := wd
110 | args := c.Args()
111 | if len(args) > 0 {
112 | bp = args[0]
113 | }
114 | builder := gin.NewBuilder(bp, c.GlobalString("bin"), c.GlobalBool("godep"))
115 | var runner gin.Runner
116 | if len(args) < 2 {
117 | runner = gin.NewRunner(builder.Binary())
118 | } else {
119 | runner = gin.NewRunner(builder.Binary(), args[1:]...)
120 | }
121 | runner.SetWriter(os.Stdout)
122 | proxy := gin.NewProxy(builder, runner)
123 | f := func(w http.ResponseWriter, r *http.Request) {
124 | runner.Kill()
125 | runner.Run()
126 | }
127 | server := httptest.NewServer(http.HandlerFunc(f))
128 | logger.info.Printf("Starting lime server at: %s\n", server.URL)
129 |
130 | if port := c.GlobalInt("port"); port > 0 {
131 | appPort := c.GlobalInt("app-port")
132 | if appPort == 0 {
133 | appPort = port + 1
134 | }
135 | config := &gin.Config{
136 | Port: port,
137 | ProxyTo: "http://localhost:" + strconv.Itoa(appPort),
138 | }
139 |
140 | if err := proxy.Run(config); err != nil {
141 | logger.warn.Fatal(err)
142 | }
143 |
144 | logger.info.Printf("listening on port %d\n", port)
145 | }
146 |
147 | shutdown(runner)
148 |
149 | // build right now
150 | build(builder, runner)
151 |
152 | // scan for changes
153 | if p := c.GlobalString("ignore-pattern"); len(p) > 0 {
154 | ipat = regexp.MustCompile(p)
155 | }
156 | if p := c.GlobalString("build-pattern"); len(p) > 0 {
157 | bpat = regexp.MustCompile(p)
158 | }
159 | if p := c.GlobalString("run-pattern"); len(p) > 0 {
160 | rpat = regexp.MustCompile(p)
161 | }
162 |
163 | targets := strings.Split(c.GlobalString("path"), ",")
164 | for {
165 | for _, target := range targets {
166 | scanChanges(filepath.Clean(filepath.Join(wd, target)), func(path string) error {
167 | ext := filepath.Ext(path)
168 | switch {
169 | case bpat != nil && bpat.MatchString(ext):
170 | logger.info.Printf("Detected file changes:\n %s", path)
171 | runner.Kill()
172 | build(builder, runner)
173 | case rpat != nil && rpat.MatchString(ext):
174 | logger.info.Printf("Detected file changes:\n %s", path)
175 | runner.Kill()
176 | runner.Run()
177 | default:
178 | return nil
179 | }
180 | buildTime = time.Now()
181 | return errors.New("done")
182 | })
183 | }
184 | time.Sleep(500 * time.Millisecond)
185 | }
186 | }
187 |
188 | func build(builder gin.Builder, runner gin.Runner) {
189 | st := time.Now()
190 | logger.info.Println("Build started")
191 | if err := builder.Build(); err != nil {
192 | logger.info.Println("ERROR! Build failed")
193 | logger.info.Println(builder.Errors())
194 | re := regexp.MustCompile("cannot find package \".*\"")
195 | matches := re.FindAllStringSubmatch(builder.Errors(), -1)
196 | goget(matches)
197 | } else {
198 | et := time.Now()
199 | logger.info.Println("Build Successful")
200 | if verbose {
201 | logger.info.Printf("Build time: %v\n", et.Sub(st))
202 | }
203 | if immediate {
204 | runner.Run()
205 | }
206 | }
207 |
208 | time.Sleep(100 * time.Millisecond)
209 | }
210 |
211 | func goget(packs [][]string) {
212 | goPath, err := exec.LookPath("go")
213 | if err != nil {
214 | logger.warn.Fatalf("Go executable not found in PATH.")
215 | }
216 | for _, pack := range packs {
217 | for _, p := range pack {
218 | rep := strings.Replace(strings.Replace(p, "cannot find package ", "", -1), `"`, "", -1)
219 | args := []string{"get", "-u", rep}
220 | cmd := exec.Command(goPath, args...)
221 | logger.info.Printf("go get -u %s\n", rep)
222 | cmd.CombinedOutput()
223 | }
224 | }
225 | }
226 |
227 | type scanCallback func(path string) error
228 |
229 | func scanChanges(watchPath string, cb scanCallback) {
230 | filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
231 | if err != nil {
232 | logger.warn.Fatal(err)
233 | return nil
234 | }
235 |
236 | if ipat != nil && ipat.MatchString(path) {
237 | return filepath.SkipDir
238 | }
239 |
240 | if info.ModTime().After(buildTime) {
241 | return cb(path)
242 | }
243 |
244 | return nil
245 | })
246 | }
247 |
248 | func shutdown(runner gin.Runner) {
249 | c := make(chan os.Signal, 2)
250 | signal.Notify(c, os.Interrupt, syscall.SIGTERM)
251 | go func() {
252 | s := <-c
253 | logger.info.Println("Got signal: ", s)
254 | err := runner.Kill()
255 | if err != nil {
256 | logger.info.Print("Error killing: ", err)
257 | }
258 | os.Exit(1)
259 | }()
260 | }
261 |
--------------------------------------------------------------------------------
/lime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kaneshin/lime/80e6eb47bb4b09b0cb2c79960ce1d2bb2eab3cdb/lime.png
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 | "net/http/httptest"
7 | "regexp"
8 | "strings"
9 |
10 | "github.com/codegangsta/cli"
11 | "github.com/codegangsta/gin/lib"
12 |
13 | "log"
14 | "os"
15 | "os/exec"
16 | "os/signal"
17 | "path/filepath"
18 | "strconv"
19 | "syscall"
20 | "time"
21 | )
22 |
23 | var (
24 | buildTime = time.Now()
25 | logger = struct {
26 | info *log.Logger
27 | warn *log.Logger
28 | }{
29 | log.New(os.Stdout, "[lime-INFO] ", log.Ldate|log.Ltime),
30 | log.New(os.Stdout, "[lime-WARN] ", log.Ldate|log.Ltime),
31 | }
32 | immediate = false
33 | verbose = false
34 | )
35 |
36 | func main() {
37 | app := cli.NewApp()
38 | app.Name = "lime"
39 | app.Usage = "A live reload utility for Go applications."
40 | app.Version = "1.0.0"
41 | app.Action = mainAction
42 | app.Flags = []cli.Flag{
43 | cli.IntFlag{
44 | Name: "port",
45 | Usage: "port for the proxy server",
46 | },
47 | cli.IntFlag{
48 | Name: "app-port",
49 | Usage: "port for the Go web server",
50 | },
51 | cli.StringFlag{
52 | Name: "bin,b",
53 | Value: "./lime-bin",
54 | Usage: "path of generated binary file",
55 | },
56 | cli.StringFlag{
57 | Name: "ignore-pattern",
58 | Usage: "pattern to ignore",
59 | },
60 | cli.StringFlag{
61 | Name: "build-pattern",
62 | Value: "(\\.go)",
63 | Usage: "pattern to build",
64 | },
65 | cli.StringFlag{
66 | Name: "run-pattern",
67 | Value: "(\\.html|\\.css|\\.js)",
68 | Usage: "pattern to run",
69 | },
70 | cli.StringFlag{
71 | Name: "path,t",
72 | Value: ".",
73 | Usage: "path to watch files from",
74 | },
75 | cli.BoolFlag{
76 | Name: "immediate,i",
77 | Usage: "run the server immediately after it's built",
78 | },
79 | cli.BoolFlag{
80 | Name: "verbose",
81 | Usage: "show verbose output",
82 | },
83 | cli.BoolFlag{
84 | Name: "godep,g",
85 | Usage: "use godep when building",
86 | },
87 | }
88 |
89 | app.Run(os.Args)
90 | }
91 |
92 | var (
93 | ipat *regexp.Regexp
94 | bpat *regexp.Regexp
95 | rpat *regexp.Regexp
96 | )
97 |
98 | func mainAction(c *cli.Context) {
99 | // TODO: New feature to check update binary is
100 | // logger.info.Printf("Skipping lime update check.\n")
101 | immediate = c.GlobalBool("immediate")
102 | verbose = c.GlobalBool("verbose")
103 |
104 | wd, err := os.Getwd()
105 | if err != nil {
106 | logger.warn.Fatal(err)
107 | }
108 |
109 | bp := wd
110 | args := c.Args()
111 | if len(args) > 0 {
112 | bp = args[0]
113 | }
114 | builder := gin.NewBuilder(bp, c.GlobalString("bin"), c.GlobalBool("godep"))
115 | var runner gin.Runner
116 | if len(args) < 2 {
117 | runner = gin.NewRunner(builder.Binary())
118 | } else {
119 | runner = gin.NewRunner(builder.Binary(), args[1:]...)
120 | }
121 | runner.SetWriter(os.Stdout)
122 | proxy := gin.NewProxy(builder, runner)
123 | f := func(w http.ResponseWriter, r *http.Request) {
124 | runner.Kill()
125 | runner.Run()
126 | }
127 | server := httptest.NewServer(http.HandlerFunc(f))
128 | logger.info.Printf("Starting lime server at: %s\n", server.URL)
129 |
130 | if port := c.GlobalInt("port"); port > 0 {
131 | appPort := c.GlobalInt("app-port")
132 | if appPort == 0 {
133 | appPort = port + 1
134 | }
135 | config := &gin.Config{
136 | Port: port,
137 | ProxyTo: "http://localhost:" + strconv.Itoa(appPort),
138 | }
139 |
140 | if err := proxy.Run(config); err != nil {
141 | logger.warn.Fatal(err)
142 | }
143 |
144 | logger.info.Printf("listening on port %d\n", port)
145 | }
146 |
147 | shutdown(runner)
148 |
149 | // build right now
150 | build(builder, runner)
151 |
152 | // scan for changes
153 | if p := c.GlobalString("ignore-pattern"); len(p) > 0 {
154 | ipat = regexp.MustCompile(p)
155 | }
156 | if p := c.GlobalString("build-pattern"); len(p) > 0 {
157 | bpat = regexp.MustCompile(p)
158 | }
159 | if p := c.GlobalString("run-pattern"); len(p) > 0 {
160 | rpat = regexp.MustCompile(p)
161 | }
162 |
163 | targets := strings.Split(c.GlobalString("path"), ",")
164 | for {
165 | for _, target := range targets {
166 | scanChanges(filepath.Clean(filepath.Join(wd, target)), func(path string) error {
167 | ext := filepath.Ext(path)
168 | switch {
169 | case bpat != nil && bpat.MatchString(ext):
170 | logger.info.Printf("Detected file changes:\n %s", path)
171 | runner.Kill()
172 | build(builder, runner)
173 | case rpat != nil && rpat.MatchString(ext):
174 | logger.info.Printf("Detected file changes:\n %s", path)
175 | runner.Kill()
176 | runner.Run()
177 | default:
178 | return nil
179 | }
180 | buildTime = time.Now()
181 | return errors.New("done")
182 | })
183 | }
184 | time.Sleep(500 * time.Millisecond)
185 | }
186 | }
187 |
188 | func build(builder gin.Builder, runner gin.Runner) {
189 | st := time.Now()
190 | logger.info.Println("Build started")
191 | if err := builder.Build(); err != nil {
192 | logger.info.Println("ERROR! Build failed")
193 | logger.info.Println(builder.Errors())
194 | re := regexp.MustCompile("cannot find package \".*\"")
195 | matches := re.FindAllStringSubmatch(builder.Errors(), -1)
196 | goget(matches)
197 | } else {
198 | et := time.Now()
199 | logger.info.Println("Build Successful")
200 | if verbose {
201 | logger.info.Printf("Build time: %v\n", et.Sub(st))
202 | }
203 | if immediate {
204 | runner.Run()
205 | }
206 | }
207 |
208 | time.Sleep(100 * time.Millisecond)
209 | }
210 |
211 | func goget(packs [][]string) {
212 | goPath, err := exec.LookPath("go")
213 | if err != nil {
214 | logger.warn.Fatalf("Go executable not found in PATH.")
215 | }
216 | for _, pack := range packs {
217 | for _, p := range pack {
218 | rep := strings.Replace(strings.Replace(p, "cannot find package ", "", -1), `"`, "", -1)
219 | args := []string{"get", "-u", rep}
220 | cmd := exec.Command(goPath, args...)
221 | logger.info.Printf("go get -u %s\n", rep)
222 | cmd.CombinedOutput()
223 | }
224 | }
225 | }
226 |
227 | type scanCallback func(path string) error
228 |
229 | func scanChanges(watchPath string, cb scanCallback) {
230 | filepath.Walk(watchPath, func(path string, info os.FileInfo, err error) error {
231 | if err != nil {
232 | logger.warn.Fatal(err)
233 | return nil
234 | }
235 |
236 | if ipat != nil && ipat.MatchString(path) {
237 | return filepath.SkipDir
238 | }
239 |
240 | if info.ModTime().After(buildTime) {
241 | return cb(path)
242 | }
243 |
244 | return nil
245 | })
246 | }
247 |
248 | func shutdown(runner gin.Runner) {
249 | c := make(chan os.Signal, 2)
250 | signal.Notify(c, os.Interrupt, syscall.SIGTERM)
251 | go func() {
252 | s := <-c
253 | logger.info.Println("Got signal: ", s)
254 | err := runner.Kill()
255 | if err != nil {
256 | logger.info.Print("Error killing: ", err)
257 | }
258 | os.Exit(1)
259 | }()
260 | }
261 |
--------------------------------------------------------------------------------
/server/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | func main() {
9 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
10 | fmt.Fprintf(w, "Hello world!\n")
11 | })
12 | http.ListenAndServe(":8181", nil)
13 | }
14 |
--------------------------------------------------------------------------------
/vendor.conf:
--------------------------------------------------------------------------------
1 | github.com/codegangsta/gin 3691f64132455125ba1d3ebdaf050a63de4e299f
2 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 |
24 | lib/test_fixtures/build_success/build_success
25 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Jeremy Saenz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/README.md:
--------------------------------------------------------------------------------
1 | gin [](https://app.wercker.com/project/bykey/f413ccbd85cfc4a58a37f03dd7aaa87e)
2 | ========
3 |
4 | `gin` is a simple command line utility for live-reloading Go web applications.
5 | Just run `gin` in your app directory and your web app will be served with
6 | `gin` as a proxy. `gin` will automatically recompile your code when it
7 | detects a change. Your app will be restarted the next time it receives an
8 | HTTP request.
9 |
10 | `gin` adheres to the "silence is golden" principle, so it will only complain
11 | if there was a compiler error or if you succesfully compile after an error.
12 |
13 | ## Installation
14 |
15 | Assuming you have a working Go environment and `GOPATH/bin` is in your
16 | `PATH`, `gin` is a breeze to install:
17 |
18 | ```shell
19 | go get github.com/codegangsta/gin
20 | ```
21 |
22 | Then verify that `gin` was installed correctly:
23 |
24 | ```shell
25 | gin -h
26 | ```
27 |
28 | ## Supporting Gin in Your Web app
29 | `gin` assumes that your web app binds itself to the `PORT` environment
30 | variable so it can properly proxy requests to your app. Web frameworks
31 | like [Martini](http://github.com/codegangsta/martini) do this out of
32 | the box.
33 |
34 | ## Using flags?
35 | When you normally start your server with [flags](https://godoc.org/flag)
36 | if you want to override any of them when running `gin` we suggest you
37 | instead use [github.com/namsral/flag](https://github.com/namsral/flag)
38 | as explained in [this post](http://stackoverflow.com/questions/24873883/organizing-environment-variables-golang/28160665#28160665)
39 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/lib/builder.go:
--------------------------------------------------------------------------------
1 | package gin
2 |
3 | import (
4 | "fmt"
5 | "os/exec"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | type Builder interface {
11 | Build() error
12 | Binary() string
13 | Errors() string
14 | }
15 |
16 | type builder struct {
17 | dir string
18 | binary string
19 | errors string
20 | useGodep bool
21 | }
22 |
23 | func NewBuilder(dir string, bin string, useGodep bool) Builder {
24 | if len(bin) == 0 {
25 | bin = "bin"
26 | }
27 |
28 | // does not work on Windows without the ".exe" extension
29 | if runtime.GOOS == "windows" {
30 | if !strings.HasSuffix(bin, ".exe") { // check if it already has the .exe extension
31 | bin += ".exe"
32 | }
33 | }
34 |
35 | return &builder{dir: dir, binary: bin, useGodep: useGodep}
36 | }
37 |
38 | func (b *builder) Binary() string {
39 | return b.binary
40 | }
41 |
42 | func (b *builder) Errors() string {
43 | return b.errors
44 | }
45 |
46 | func (b *builder) Build() error {
47 | var command *exec.Cmd
48 | if b.useGodep {
49 | command = exec.Command("godep", "go", "build", "-o", b.binary)
50 | } else {
51 | command = exec.Command("go", "build", "-o", b.binary)
52 | }
53 | command.Dir = b.dir
54 |
55 | output, err := command.CombinedOutput()
56 |
57 | if command.ProcessState.Success() {
58 | b.errors = ""
59 | } else {
60 | b.errors = string(output)
61 | }
62 |
63 | if len(b.errors) > 0 {
64 | return fmt.Errorf(b.errors)
65 | }
66 |
67 | return err
68 | }
69 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/lib/config.go:
--------------------------------------------------------------------------------
1 | package gin
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | type Config struct {
10 | Laddr string `json:"laddr"`
11 | Port int `json:"port"`
12 | ProxyTo string `json:"proxy_to"`
13 | }
14 |
15 | func LoadConfig(path string) (*Config, error) {
16 | configFile, err := os.Open(path)
17 |
18 | if err != nil {
19 | return nil, fmt.Errorf("Unable to read configuration file %s", path)
20 | }
21 |
22 | config := new(Config)
23 |
24 | decoder := json.NewDecoder(configFile)
25 | err = decoder.Decode(&config)
26 | if err != nil {
27 | return nil, fmt.Errorf("Unable to parse configuration file %s", path)
28 | }
29 |
30 | return config, nil
31 | }
32 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/lib/proxy.go:
--------------------------------------------------------------------------------
1 | package gin
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net"
7 | "net/http"
8 | "net/http/httputil"
9 | "net/url"
10 | "strings"
11 | )
12 |
13 | type Proxy struct {
14 | listener net.Listener
15 | proxy *httputil.ReverseProxy
16 | builder Builder
17 | runner Runner
18 | to *url.URL
19 | }
20 |
21 | func NewProxy(builder Builder, runner Runner) *Proxy {
22 | return &Proxy{
23 | builder: builder,
24 | runner: runner,
25 | }
26 | }
27 |
28 | func (p *Proxy) Run(config *Config) error {
29 |
30 | // create our reverse proxy
31 | url, err := url.Parse(config.ProxyTo)
32 | if err != nil {
33 | return err
34 | }
35 | p.proxy = httputil.NewSingleHostReverseProxy(url)
36 | p.to = url
37 |
38 | p.listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", config.Laddr, config.Port))
39 | if err != nil {
40 | return err
41 | }
42 |
43 | go http.Serve(p.listener, http.HandlerFunc(p.defaultHandler))
44 | return nil
45 | }
46 |
47 | func (p *Proxy) Close() error {
48 | return p.listener.Close()
49 | }
50 |
51 | func (p *Proxy) defaultHandler(res http.ResponseWriter, req *http.Request) {
52 | errors := p.builder.Errors()
53 | if len(errors) > 0 {
54 | res.Write([]byte(errors))
55 | } else {
56 | p.runner.Run()
57 | if strings.ToLower(req.Header.Get("Upgrade")) == "websocket" || strings.ToLower(req.Header.Get("Accept")) == "text/event-stream" {
58 | proxyWebsocket(res, req, p.to)
59 | } else {
60 | p.proxy.ServeHTTP(res, req)
61 | }
62 | }
63 | }
64 |
65 | func proxyWebsocket(w http.ResponseWriter, r *http.Request, host *url.URL) {
66 | d, err := net.Dial("tcp", host.Host)
67 | if err != nil {
68 | http.Error(w, "Error contacting backend server.", 500)
69 | fmt.Errorf("Error dialing websocket backend %s: %v", host, err)
70 | return
71 | }
72 | hj, ok := w.(http.Hijacker)
73 | if !ok {
74 | http.Error(w, "Not a hijacker?", 500)
75 | return
76 | }
77 | nc, _, err := hj.Hijack()
78 | if err != nil {
79 | fmt.Errorf("Hijack error: %v", err)
80 | return
81 | }
82 | defer nc.Close()
83 | defer d.Close()
84 |
85 | err = r.Write(d)
86 | if err != nil {
87 | fmt.Errorf("Error copying request to target: %v", err)
88 | return
89 | }
90 |
91 | errc := make(chan error, 2)
92 | cp := func(dst io.Writer, src io.Reader) {
93 | _, err := io.Copy(dst, src)
94 | errc <- err
95 | }
96 | go cp(d, nc)
97 | go cp(nc, d)
98 | <-errc
99 | }
100 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/lib/runner.go:
--------------------------------------------------------------------------------
1 | package gin
2 |
3 | import (
4 | "io"
5 | "io/ioutil"
6 | "log"
7 | "os"
8 | "os/exec"
9 | "runtime"
10 | "time"
11 | )
12 |
13 | type Runner interface {
14 | Run() (*exec.Cmd, error)
15 | Info() (os.FileInfo, error)
16 | SetWriter(io.Writer)
17 | Kill() error
18 | }
19 |
20 | type runner struct {
21 | bin string
22 | args []string
23 | writer io.Writer
24 | command *exec.Cmd
25 | starttime time.Time
26 | }
27 |
28 | func NewRunner(bin string, args ...string) Runner {
29 | return &runner{
30 | bin: bin,
31 | args: args,
32 | writer: ioutil.Discard,
33 | starttime: time.Now(),
34 | }
35 | }
36 |
37 | func (r *runner) Run() (*exec.Cmd, error) {
38 | if r.needsRefresh() {
39 | r.Kill()
40 | }
41 |
42 | if r.command == nil || r.Exited() {
43 | err := r.runBin()
44 | time.Sleep(250 * time.Millisecond)
45 | return r.command, err
46 | } else {
47 | return r.command, nil
48 | }
49 |
50 | }
51 |
52 | func (r *runner) Info() (os.FileInfo, error) {
53 | return os.Stat(r.bin)
54 | }
55 |
56 | func (r *runner) SetWriter(writer io.Writer) {
57 | r.writer = writer
58 | }
59 |
60 | func (r *runner) Kill() error {
61 | if r.command != nil && r.command.Process != nil {
62 | done := make(chan error)
63 | go func() {
64 | r.command.Wait()
65 | close(done)
66 | }()
67 |
68 | //Trying a "soft" kill first
69 | if runtime.GOOS == "windows" {
70 | if err := r.command.Process.Kill(); err != nil {
71 | return err
72 | }
73 | } else if err := r.command.Process.Signal(os.Interrupt); err != nil {
74 | return err
75 | }
76 |
77 | //Wait for our process to die before we return or hard kill after 3 sec
78 | select {
79 | case <-time.After(3 * time.Second):
80 | if err := r.command.Process.Kill(); err != nil {
81 | log.Println("failed to kill: ", err)
82 | }
83 | case <-done:
84 | }
85 | r.command = nil
86 | }
87 |
88 | return nil
89 | }
90 |
91 | func (r *runner) Exited() bool {
92 | return r.command != nil && r.command.ProcessState != nil && r.command.ProcessState.Exited()
93 | }
94 |
95 | func (r *runner) runBin() error {
96 | r.command = exec.Command(r.bin, r.args...)
97 | stdout, err := r.command.StdoutPipe()
98 | if err != nil {
99 | return err
100 | }
101 | stderr, err := r.command.StderrPipe()
102 | if err != nil {
103 | return err
104 | }
105 |
106 | err = r.command.Start()
107 | if err != nil {
108 | return err
109 | }
110 |
111 | r.starttime = time.Now()
112 |
113 | go io.Copy(r.writer, stdout)
114 | go io.Copy(r.writer, stderr)
115 | go r.command.Wait()
116 |
117 | return nil
118 | }
119 |
120 | func (r *runner) needsRefresh() bool {
121 | info, err := r.Info()
122 | if err != nil {
123 | return false
124 | } else {
125 | return info.ModTime().After(r.starttime)
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/vendor/github.com/codegangsta/gin/wercker.yml:
--------------------------------------------------------------------------------
1 | box: wercker/golang@1.1.1
2 |
--------------------------------------------------------------------------------
/wercker.yml:
--------------------------------------------------------------------------------
1 | box: google/golang
2 |
3 | # Build definition
4 | build:
5 | # The steps that will be executed on build
6 | steps:
7 |
8 | # Sets the go workspace and places you package
9 | # at the right place in the workspace tree
10 | - setup-go-workspace
11 |
12 | # golint step!
13 | - wercker/golint
14 |
15 | # goget
16 | - script:
17 | name: go get
18 | code: |
19 | cd $WERCKER_SOURCE_DIR
20 | go get -d ./...
21 |
22 | # Build the project
23 | - script:
24 | name: go build
25 | code: |
26 | go build ./...
27 |
28 | # Test the project
29 | - script:
30 | name: go test
31 | code: |
32 | go test ./...
33 |
34 |
--------------------------------------------------------------------------------