Skip to content

Commit 147021b

Browse files
authored
Merge branch 'master' into patch-1
2 parents 6e1ae9f + 9e14bf4 commit 147021b

File tree

8 files changed

+114
-35
lines changed

8 files changed

+114
-35
lines changed

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ air
9595

9696
For modifying the configuration refer to the [air_example.toml](air_example.toml) file.
9797

98+
### Docker-compose
99+
100+
```
101+
services:
102+
my-project-with-air:
103+
image: cosmtrek/air
104+
# working_dir value has to be the same of mapped volume
105+
working_dir: /project-package
106+
ports:
107+
- <any>:<any>
108+
environment:
109+
- ENV_A=${ENV_A}
110+
- ENV_B=${ENV_B}
111+
- ENV_C=${ENV_C}
112+
volumes:
113+
- ./project-relative-path/:/project-package/
114+
```
98115

99116
### Debug
100117

air_example.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ tmp_dir = "tmp"
1010
cmd = "go build -o ./tmp/main ."
1111
# Binary file yields from `cmd`.
1212
bin = "tmp/main"
13-
# Customize binary.
13+
# Customize binary, can setup environment variables when run your app.
1414
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
1515
# Watch these filename extensions.
1616
include_ext = ["go", "tpl", "tmpl", "html"]

install.sh

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ get_binaries() {
6666
darwin/arm64) BINARIES="air" ;;
6767
linux/386) BINARIES="air" ;;
6868
linux/amd64) BINARIES="air" ;;
69+
linux/arm64) BINARIES="air" ;;
6970
windows/386) BINARIES="air" ;;
7071
windows/amd64) BINARIES="air" ;;
7172
*)

runner/config.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ const (
2121
)
2222

2323
type config struct {
24-
Root string `toml:"root"`
25-
TmpDir string `toml:"tmp_dir"`
26-
TestDataDir string `toml:"testdata_dir"`
27-
Build cfgBuild `toml:"build"`
28-
Color cfgColor `toml:"color"`
29-
Log cfgLog `toml:"log"`
30-
Misc cfgMisc `toml:"misc"`
24+
Root string `toml:"root"`
25+
TmpDir string `toml:"tmp_dir"`
26+
TestDataDir string `toml:"testdata_dir"`
27+
Build cfgBuild `toml:"build"`
28+
Color cfgColor `toml:"color"`
29+
Log cfgLog `toml:"log"`
30+
Misc cfgMisc `toml:"misc"`
31+
Screen cfgScreen `toml:"screen"`
3132
}
3233

3334
type cfgBuild struct {
@@ -79,6 +80,10 @@ type cfgMisc struct {
7980
CleanOnExit bool `toml:"clean_on_exit"`
8081
}
8182

83+
type cfgScreen struct {
84+
ClearOnRebuild bool `toml:"clear_on_rebuild"`
85+
}
86+
8287
func initConfig(path string) (cfg *config, err error) {
8388
if path == "" {
8489
cfg, err = defaultPathConfig()

runner/engine.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runner
22

33
import (
4+
"fmt"
45
"io"
56
"os"
67
"os/exec"
@@ -144,6 +145,9 @@ func (e *Engine) watching(root string) error {
144145
func (e *Engine) cacheFileChecksums(root string) error {
145146
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
146147
if err != nil {
148+
if info == nil {
149+
return err
150+
}
147151
if info.IsDir() {
148152
return filepath.SkipDir
149153
}
@@ -199,7 +203,7 @@ func (e *Engine) cacheFileChecksums(root string) error {
199203

200204
func (e *Engine) watchDir(path string) error {
201205
if err := e.watcher.Add(path); err != nil {
202-
e.watcherLog("failed to watching %s, error: %s", path, err.Error())
206+
e.watcherLog("failed to watch %s, error: %s", path, err.Error())
203207
return err
204208
}
205209
e.watcherLog("watching %s", e.config.rel(path))
@@ -314,19 +318,29 @@ func (e *Engine) start() {
314318
continue
315319
}
316320
}
321+
317322
time.Sleep(e.config.buildDelay())
318323
e.flushEvents()
324+
325+
// clean on rebuild https://stackoverflow.com/questions/22891644/how-can-i-clear-the-terminal-screen-in-go
326+
if e.config.Screen.ClearOnRebuild {
327+
fmt.Println("\033[2J")
328+
}
329+
319330
e.mainLog("%s has changed", e.config.rel(filename))
320331
case <-firstRunCh:
321332
// go down
322333
break
323334
}
324335

336+
// already build and run now
325337
select {
326338
case <-e.buildRunCh:
327339
e.buildRunStopCh <- true
328340
default:
329341
}
342+
343+
// if current app is running, stop it
330344
e.withLock(func() {
331345
if e.binRunning {
332346
e.binStopCh <- true
@@ -382,16 +396,16 @@ func (e *Engine) flushEvents() {
382396
func (e *Engine) building() error {
383397
var err error
384398
e.buildLog("building...")
385-
cmd, stdout, stderr, err := e.startCmd(e.config.Build.Cmd)
399+
cmd, stdin, stdout, stderr, err := e.startCmd(e.config.Build.Cmd)
386400
if err != nil {
387401
return err
388402
}
389403
defer func() {
390404
stdout.Close()
391405
stderr.Close()
406+
stdin.Close()
392407
}()
393-
_, _ = io.Copy(os.Stdout, stdout)
394-
_, _ = io.Copy(os.Stderr, stderr)
408+
395409
// wait for building
396410
err = cmd.Wait()
397411
if err != nil {
@@ -403,20 +417,15 @@ func (e *Engine) building() error {
403417
func (e *Engine) runBin() error {
404418
var err error
405419
e.runnerLog("running...")
406-
cmd, stdout, stderr, err := e.startCmd(e.config.Build.Bin)
420+
cmd, stdin, stdout, stderr, err := e.startCmd(e.config.Build.Bin)
407421
if err != nil {
408422
return err
409423
}
410424
e.withLock(func() {
411425
e.binRunning = true
412426
})
413427

414-
go func() {
415-
_, _ = io.Copy(os.Stdout, stdout)
416-
_, _ = io.Copy(os.Stderr, stderr)
417-
}()
418-
419-
go func(cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser) {
428+
go func(cmd *exec.Cmd, stdin io.WriteCloser, stdout io.ReadCloser, stderr io.ReadCloser) {
420429
defer func() {
421430
select {
422431
case <-e.exitCh:
@@ -429,6 +438,7 @@ func (e *Engine) runBin() error {
429438
defer func() {
430439
stdout.Close()
431440
stderr.Close()
441+
stdin.Close()
432442
}()
433443
pid, err := e.killCmd(cmd)
434444
if err != nil {
@@ -449,7 +459,7 @@ func (e *Engine) runBin() error {
449459
if err = os.Remove(cmdBinPath); err != nil {
450460
e.mainLog("failed to remove %s, error: %s", e.config.rel(e.config.binPath()), err)
451461
}
452-
}(cmd, stdout, stderr)
462+
}(cmd, stdin, stdout, stderr)
453463
return nil
454464
}
455465

runner/util_darwin.go

+24-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package runner
22

33
import (
44
"io"
5+
"os"
56
"os/exec"
67
"syscall"
78
"time"
8-
9-
"github.com/creack/pty"
109
)
1110

1211
func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
@@ -26,8 +25,28 @@ func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
2625
return pid, err
2726
}
2827

29-
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
28+
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
3029
c := exec.Command("/bin/sh", "-c", cmd)
31-
f, err := pty.Start(c)
32-
return c, f, f, err
30+
stderr, err := c.StderrPipe()
31+
if err != nil {
32+
return nil, nil, nil, nil, err
33+
}
34+
stdout, err := c.StdoutPipe()
35+
if err != nil {
36+
return nil, nil, nil, nil, err
37+
}
38+
stdin, err := c.StdinPipe()
39+
if err != nil {
40+
return nil, nil, nil, nil, err
41+
}
42+
43+
c.Stdout = os.Stdout
44+
c.Stderr = os.Stderr
45+
c.Stdin = os.Stdin
46+
47+
err = c.Start()
48+
if err != nil {
49+
return nil, nil, nil, nil, err
50+
}
51+
return c, stdin, stdout, stderr, nil
3352
}

runner/util_linux.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package runner
22

33
import (
44
"io"
5+
"os"
56
"os/exec"
67
"syscall"
78
"time"
8-
9-
"github.com/creack/pty"
109
)
1110

1211
func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
@@ -28,8 +27,27 @@ func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
2827
return
2928
}
3029

31-
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
30+
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
3231
c := exec.Command("/bin/sh", "-c", cmd)
33-
f, err := pty.Start(c)
34-
return c, f, f, err
32+
stderr, err := c.StderrPipe()
33+
if err != nil {
34+
return nil, nil, nil, nil, err
35+
}
36+
stdout, err := c.StdoutPipe()
37+
if err != nil {
38+
return nil, nil, nil, nil, err
39+
}
40+
stdin, err := c.StdinPipe()
41+
if err != nil {
42+
return nil, nil, nil, nil, err
43+
}
44+
c.Stdout = os.Stdout
45+
c.Stderr = os.Stderr
46+
c.Stdin = os.Stdin
47+
48+
err = c.Start()
49+
if err != nil {
50+
return nil, nil, nil, nil, err
51+
}
52+
return c, stdin, stdout, stderr, nil
3553
}

runner/util_windows.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runner
22

33
import (
44
"io"
5+
"os"
56
"os/exec"
67
"strconv"
78
"strings"
@@ -14,7 +15,7 @@ func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
1415
return pid, kill.Run()
1516
}
1617

17-
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
18+
func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
1819
var err error
1920

2021
if !strings.Contains(cmd, ".exe") {
@@ -24,15 +25,23 @@ func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.ReadCloser, io.ReadCloser,
2425
c := exec.Command("cmd", "/c", cmd)
2526
stderr, err := c.StderrPipe()
2627
if err != nil {
27-
return nil, nil, nil, err
28+
return nil, nil, nil, nil, err
2829
}
2930
stdout, err := c.StdoutPipe()
3031
if err != nil {
31-
return nil, nil, nil, err
32+
return nil, nil, nil, nil, err
3233
}
34+
stdin, err := c.StdinPipe()
35+
if err != nil {
36+
return nil, nil, nil, nil, err
37+
}
38+
c.Stdout = os.Stdout
39+
c.Stderr = os.Stderr
40+
c.Stdin = os.Stdin
41+
3342
err = c.Start()
3443
if err != nil {
35-
return nil, nil, nil, err
44+
return nil, nil, nil, nil, err
3645
}
37-
return c, stdout, stderr, err
46+
return c, stdin, stdout, stderr, nil
3847
}

0 commit comments

Comments
 (0)