Skip to content

Commit 3a05732

Browse files
authored
Merge pull request [#7](#7)
2 parents 70c2485 + 67f7416 commit 3a05732

File tree

12 files changed

+528
-429
lines changed

12 files changed

+528
-429
lines changed

Taskfile.yml

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ tasks:
3030
upx ./bin/{{.binary}}
3131
fi
3232
33+
build:dev:
34+
cmds:
35+
- go build -o ./bin/fwatcher-dev ./cmd
36+
3337
example:http-server:
3438
cmds:
3539
- |+

cmd/main.go

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
"os/exec"
9+
"os/signal"
10+
"path/filepath"
11+
"strings"
12+
"sync"
13+
"syscall"
14+
"time"
15+
16+
"github.com/nxtcoder17/fwatcher/pkg/executor"
17+
fn "github.com/nxtcoder17/fwatcher/pkg/functions"
18+
"github.com/nxtcoder17/fwatcher/pkg/logging"
19+
"github.com/nxtcoder17/fwatcher/pkg/watcher"
20+
"github.com/urfave/cli/v3"
21+
)
22+
23+
var (
24+
ProgramName string
25+
Version string
26+
)
27+
28+
// DefaultIgnoreList is list of directories that are mostly ignored
29+
var DefaultIgnoreList = []string{
30+
".git", ".svn", ".hg", // version control
31+
".idea", ".vscode", // IDEs
32+
".direnv", // direnv nix
33+
"node_modules", // node
34+
".DS_Store", // macOS
35+
".log", // logs
36+
}
37+
38+
func main() {
39+
cmd := &cli.Command{
40+
Name: ProgramName,
41+
UseShortOptionHandling: true,
42+
Usage: "simple tool to run commands on filesystem change events",
43+
ArgsUsage: "<Command To Run>",
44+
Version: Version,
45+
Flags: []cli.Flag{
46+
&cli.BoolFlag{
47+
Name: "debug",
48+
},
49+
50+
&cli.StringFlag{
51+
Name: "command",
52+
Usage: "[command to run]",
53+
Value: "echo hi",
54+
Aliases: []string{"c"},
55+
},
56+
57+
&cli.StringSliceFlag{
58+
Name: "watch",
59+
Usage: "[dir] (to watch) | -[dir] (to ignore)",
60+
Value: []string{"."},
61+
Aliases: []string{"w"},
62+
},
63+
64+
&cli.StringSliceFlag{
65+
Name: "ext",
66+
Usage: "[ext] (to watch) | -[ext] (to ignore)",
67+
Required: false,
68+
Aliases: []string{"e"},
69+
},
70+
71+
// &cli.StringSliceFlag{
72+
// Name: "exclude",
73+
// Usage: "exclude this directory",
74+
// Aliases: []string{"x"},
75+
// },
76+
77+
&cli.StringSliceFlag{
78+
Name: "ignore-list",
79+
Usage: "disables ignoring from default ignore list",
80+
Value: DefaultIgnoreList,
81+
Aliases: []string{"I"},
82+
},
83+
84+
&cli.StringFlag{
85+
Name: "cooldown",
86+
Usage: "cooldown duration",
87+
Value: "100ms",
88+
},
89+
90+
&cli.BoolFlag{
91+
Name: "interactive",
92+
Usage: "interactive mode, with stdin",
93+
},
94+
95+
&cli.BoolFlag{
96+
Name: "sse",
97+
Usage: "run watcher in sse mode",
98+
},
99+
100+
&cli.StringFlag{
101+
Name: "sse-addr",
102+
HideDefault: false,
103+
Usage: "run watcher in sse mode",
104+
Sources: cli.ValueSourceChain{},
105+
Value: ":12345",
106+
},
107+
},
108+
Action: func(ctx context.Context, c *cli.Command) error {
109+
logger := logging.NewSlogLogger(logging.SlogOptions{
110+
ShowTimestamp: false,
111+
ShowCaller: false,
112+
ShowDebugLogs: c.Bool("debug"),
113+
SetAsDefaultLogger: true,
114+
})
115+
116+
if c.NArg() == 0 {
117+
return c.Command("help").Action(ctx, c)
118+
}
119+
120+
var watchDirs, excludeDirs []string
121+
122+
for _, d := range c.StringSlice("watch") {
123+
if strings.HasPrefix(d, "-") {
124+
// INFO: needs to be excluded
125+
excludeDirs = append(excludeDirs, d[1:])
126+
continue
127+
}
128+
watchDirs = append(watchDirs, d)
129+
}
130+
131+
var watchExtensions, ignoreExtensions []string
132+
for _, ext := range c.StringSlice("ext") {
133+
if strings.HasPrefix(ext, "-") {
134+
// INFO: needs to be excluded
135+
ignoreExtensions = append(ignoreExtensions, ext[1:])
136+
continue
137+
}
138+
watchExtensions = append(watchExtensions, ext)
139+
}
140+
141+
cooldown, err := time.ParseDuration(c.String("cooldown"))
142+
if err != nil {
143+
panic(err)
144+
}
145+
146+
args := watcher.WatcherArgs{
147+
Logger: logger,
148+
149+
WatchDirs: watchDirs,
150+
IgnoreDirs: excludeDirs,
151+
152+
WatchExtensions: watchExtensions,
153+
IgnoreExtensions: ignoreExtensions,
154+
CooldownDuration: &cooldown,
155+
156+
IgnoreList: c.StringSlice("ignore-list"),
157+
}
158+
159+
w, err := watcher.NewWatcher(ctx, args)
160+
if err != nil {
161+
panic(err)
162+
}
163+
164+
var ex executor.Executor
165+
166+
switch {
167+
case c.Bool("sse"):
168+
{
169+
sseAddr := c.String("sse-addr")
170+
ex = executor.NewSSEExecutor(executor.SSEExecutorArgs{Addr: sseAddr})
171+
logger.Info("HELLo world")
172+
}
173+
default:
174+
{
175+
execCmd := c.Args().First()
176+
execArgs := c.Args().Tail()
177+
ex = executor.NewCmdExecutor(ctx, executor.CmdExecutorArgs{
178+
Logger: logger,
179+
Interactive: c.Bool("interactive"),
180+
Command: func(context.Context) *exec.Cmd {
181+
cmd := exec.CommandContext(ctx, execCmd, execArgs...)
182+
cmd.Stdout = os.Stdout
183+
cmd.Stderr = os.Stderr
184+
cmd.Stdin = os.Stdin
185+
return cmd
186+
},
187+
// IsInteractive: true,
188+
})
189+
}
190+
}
191+
192+
var wg sync.WaitGroup
193+
wg.Add(1)
194+
go func() {
195+
defer wg.Done()
196+
if err := ex.Start(); err != nil {
197+
slog.Error("got", "err", err)
198+
}
199+
logger.Debug("1. start-job finished")
200+
}()
201+
202+
counter := 0
203+
pwd := fn.Must(os.Getwd())
204+
205+
wg.Add(1)
206+
go func() {
207+
defer wg.Done()
208+
w.Watch(ctx)
209+
logger.Debug("2. watch context closed")
210+
}()
211+
212+
wg.Add(1)
213+
go func() {
214+
defer wg.Done()
215+
<-ctx.Done()
216+
ex.Stop()
217+
logger.Debug("3. killed signal processed")
218+
}()
219+
220+
for event := range w.GetEvents() {
221+
logger.Debug("received", "event", event)
222+
relPath, err := filepath.Rel(pwd, event.Name)
223+
if err != nil {
224+
return err
225+
}
226+
counter += 1
227+
logger.Info(fmt.Sprintf("[RELOADING (%d)] due changes in %s", counter, relPath))
228+
229+
ex.OnWatchEvent(executor.Event{Source: event.Name})
230+
}
231+
232+
// logger.Debug("stopping executor")
233+
// if err := ex.Stop(); err != nil {
234+
// return err
235+
// }
236+
// logger.Info("stopped executor")
237+
238+
wg.Wait()
239+
return nil
240+
},
241+
}
242+
243+
ctx, stop := signal.NotifyContext(context.TODO(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT)
244+
defer stop()
245+
246+
if err := cmd.Run(ctx, os.Args); err != nil {
247+
panic(err)
248+
}
249+
os.Exit(0)
250+
}

go.mod

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,18 @@ require (
66
github.com/charmbracelet/lipgloss v0.13.0
77
github.com/charmbracelet/log v0.4.0
88
github.com/fsnotify/fsnotify v1.6.0
9-
github.com/urfave/cli/v2 v2.24.3
9+
github.com/urfave/cli/v3 v3.0.0-beta1
1010
)
1111

1212
require (
1313
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
1414
github.com/charmbracelet/x/ansi v0.1.4 // indirect
15-
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
1615
github.com/go-logfmt/logfmt v0.6.0 // indirect
1716
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
1817
github.com/mattn/go-isatty v0.0.20 // indirect
1918
github.com/mattn/go-runewidth v0.0.15 // indirect
2019
github.com/muesli/termenv v0.15.2 // indirect
2120
github.com/rivo/uniseg v0.4.7 // indirect
22-
github.com/russross/blackfriday/v2 v2.1.0 // indirect
23-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
2421
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
2522
golang.org/x/sys v0.19.0 // indirect
2623
)

go.sum

+2-8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8
66
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
77
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
88
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
9-
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
10-
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
119
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1210
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
1311
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@@ -25,13 +23,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
2523
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
2624
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
2725
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
28-
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
29-
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
3026
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
31-
github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0=
32-
github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
33-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
34-
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
27+
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
28+
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
3529
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
3630
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
3731
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

0 commit comments

Comments
 (0)