@@ -4,6 +4,11 @@ import (
44 "fmt"
55 "io"
66 "net/http"
7+ "os"
8+ "os/signal"
9+ "path/filepath"
10+
11+ "github.com/rjeczalik/notify"
712
813 "github.com/kosli-dev/cli/internal/requests"
914 "github.com/kosli-dev/cli/internal/server"
@@ -41,6 +46,7 @@ type snapshotPathOptions struct {
4146 path string
4247 artifactName string
4348 exclude []string
49+ watch bool
4450}
4551
4652func newSnapshotPathCmd (out io.Writer ) * cobra.Command {
@@ -67,6 +73,7 @@ func newSnapshotPathCmd(out io.Writer) *cobra.Command {
6773 cmd .Flags ().StringVar (& o .path , "path" , "" , snapshotPathPathFlag )
6874 cmd .Flags ().StringVar (& o .artifactName , "name" , "" , snapshotPathArtifactNameFlag )
6975 cmd .Flags ().StringSliceVarP (& o .exclude , "exclude" , "x" , []string {}, snapshotPathExcludeFlag )
76+ cmd .Flags ().BoolVar (& o .watch , "watch" , false , pathsWatchFlag )
7077 addDryRunFlag (cmd )
7178
7279 if err := RequireFlags (cmd , []string {"path" , "name" }); err != nil {
@@ -78,9 +85,6 @@ func newSnapshotPathCmd(out io.Writer) *cobra.Command {
7885
7986func (o * snapshotPathOptions ) run (args []string ) error {
8087 envName := args [0 ]
81-
82- url := fmt .Sprintf ("%s/api/v2/environments/%s/%s/report/server" , global .Host , global .Org , envName )
83-
8488 // load path spec from flags
8589 ps := & server.PathsSpec {
8690 Version : 1 ,
@@ -92,6 +96,23 @@ func (o *snapshotPathOptions) run(args []string) error {
9296 },
9397 }
9498
99+ err := reportArtifacts (ps , envName )
100+ if err != nil {
101+ return err
102+ }
103+
104+ if o .watch {
105+ err := watchPath (ps , o .path , envName )
106+ if err != nil {
107+ return err
108+ }
109+ }
110+
111+ return nil
112+ }
113+
114+ func reportArtifacts (ps * server.PathsSpec , envName string ) error {
115+ url := fmt .Sprintf ("%s/api/v2/environments/%s/%s/report/server" , global .Host , global .Org , envName )
95116 artifacts , err := server .CreatePathsArtifactsData (ps , logger )
96117 if err != nil {
97118 return err
@@ -113,3 +134,63 @@ func (o *snapshotPathOptions) run(args []string) error {
113134 }
114135 return err
115136}
137+
138+ func watchPath (ps * server.PathsSpec , path , envName string ) error {
139+ events := make (chan notify.EventInfo , 1 )
140+ if err := watchRecursive (path , events ); err != nil {
141+ return fmt .Errorf ("error setting up watcher: %v" , err )
142+ }
143+ defer notify .Stop (events )
144+
145+ logger .Info ("watching for file changes in %s. Press Ctrl+C to exit..." , path )
146+
147+ // Handle system interrupts (Ctrl+C)
148+ stop := make (chan os.Signal , 1 )
149+ signal .Notify (stop , os .Interrupt )
150+
151+ for {
152+ select {
153+ case event := <- events :
154+ logger .Debug ("event: %s on %s" , event .Event (), event .Path ())
155+
156+ err := reportArtifacts (ps , envName )
157+ if err != nil {
158+ return err
159+ }
160+
161+ // If a new directory is created, start watching it
162+ // github.com/rjeczalik/notify does not automatically watch new subdirs
163+ if event .Event () == notify .Create {
164+ info , err := os .Stat (event .Path ())
165+ if err == nil && info .IsDir () {
166+ logger .Debug ("new directory detected: %s. Adding to watcher." , event .Path ())
167+ err = notify .Watch (event .Path ()+ "/..." , events , notify .All )
168+ if err != nil {
169+ return err
170+ }
171+ }
172+ }
173+ case <- stop :
174+ logger .Info ("\t stopping file watcher..." )
175+ return nil
176+ }
177+ }
178+ }
179+
180+ // Watches a directory recursively and detects new subdirectories
181+ func watchRecursive (root string , events chan notify.EventInfo ) error {
182+ return filepath .Walk (root , func (path string , info os.FileInfo , err error ) error {
183+ if err != nil {
184+ return err
185+ }
186+ // Watch all directories, including existing and newly created ones
187+ if info .IsDir () {
188+ if err := notify .Watch (path + "/..." , events , notify .All ); err != nil {
189+ return fmt .Errorf ("failed to watch %s: %v" , path , err )
190+ } else {
191+ logger .Debug ("watching: %s" , path )
192+ }
193+ }
194+ return nil
195+ })
196+ }
0 commit comments