Browse Source

feat: monitor glance.yml for changes

teemozhu 1 year ago
parent
commit
1600007f70
6 changed files with 87 additions and 16 deletions
  1. 2 0
      .gitignore
  2. 2 0
      go.mod
  3. 4 0
      go.sum
  4. 57 0
      internal/glance/config.go
  5. 15 4
      internal/glance/glance.go
  6. 7 12
      internal/glance/main.go

+ 2 - 0
.gitignore

@@ -1,4 +1,6 @@
 /assets
 /build
 /playground
+.idea
+.vscode
 glance.yml

+ 2 - 0
go.mod

@@ -11,9 +11,11 @@ require (
 require (
 	github.com/PuerkitoBio/goquery v1.9.1 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/mmcdole/goxpp v1.1.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	golang.org/x/net v0.24.0 // indirect
+	golang.org/x/sys v0.19.0 // indirect
 )

+ 4 - 0
go.sum

@@ -5,6 +5,8 @@ github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@@ -45,6 +47,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

+ 57 - 0
internal/glance/config.go

@@ -1,8 +1,11 @@
 package glance
 
 import (
+	"errors"
 	"fmt"
+	"github.com/fsnotify/fsnotify"
 	"io"
+	"os"
 
 	"gopkg.in/yaml.v3"
 )
@@ -35,6 +38,19 @@ func NewConfigFromYml(contents io.Reader) (*Config, error) {
 	return config, nil
 }
 
+// NewConfigFromFile reads a yaml file and returns a Config struct
+func NewConfigFromFile(path string) (*Config, error) {
+	configFile, err := os.Open(path)
+
+	if err != nil {
+		return nil, errors.New("failed opening config file: " + err.Error())
+	}
+
+	defer configFile.Close()
+
+	return NewConfigFromYml(configFile)
+}
+
 func NewConfig() *Config {
 	config := &Config{}
 
@@ -77,3 +93,44 @@ func configIsValid(config *Config) error {
 
 	return nil
 }
+
+func watchConfigFile(configPath string, f func(*Config) error) {
+	watcher, err := fsnotify.NewWatcher()
+	if err != nil {
+		fmt.Printf("failed creating watcher: %v\n", err)
+		return
+	}
+
+	go func() {
+		for {
+			select {
+			case event, ok := <-watcher.Events:
+				if !ok {
+					return
+				}
+				if event.Op&fsnotify.Write == fsnotify.Write {
+					config, err := NewConfigFromFile(configPath)
+					if err != nil {
+						fmt.Printf("failed loading config file: %v\n", err)
+						return
+					}
+					err = f(config)
+					if err != nil {
+						fmt.Printf("failed applying new config: %v\n", err)
+					}
+				}
+			case err, ok := <-watcher.Errors:
+				if !ok {
+					return
+				}
+				_ = watcher.Close()
+				fmt.Printf("watcher error: %v\n", err)
+			}
+		}
+	}()
+
+	err = watcher.Add(configPath)
+	if err != nil {
+		fmt.Printf("failed adding watcher: %v\n", err)
+	}
+}

+ 15 - 4
internal/glance/glance.go

@@ -97,6 +97,10 @@ func titleToSlug(s string) string {
 }
 
 func NewApplication(config *Config) (*Application, error) {
+	// make sure the config is valid
+	if config == nil {
+		return nil, fmt.Errorf("config is nil")
+	}
 	if len(config.Pages) == 0 {
 		return nil, fmt.Errorf("no pages configured")
 	}
@@ -107,17 +111,24 @@ func NewApplication(config *Config) (*Application, error) {
 		slugToPage: make(map[string]*Page),
 	}
 
-	app.slugToPage[""] = &config.Pages[0]
+	app.Reload(config)
+
+	return app, nil
+}
+
+// Reload updates the application with a new config
+func (a *Application) Reload(config *Config) {
+	a.Config = *config
+
+	a.slugToPage[""] = &config.Pages[0]
 
 	for i := range config.Pages {
 		if config.Pages[i].Slug == "" {
 			config.Pages[i].Slug = titleToSlug(config.Pages[i].Title)
 		}
 
-		app.slugToPage[config.Pages[i].Slug] = &config.Pages[i]
+		a.slugToPage[config.Pages[i].Slug] = &config.Pages[i]
 	}
-
-	return app, nil
 }
 
 func (a *Application) HandlePageRequest(w http.ResponseWriter, r *http.Request) {

+ 7 - 12
internal/glance/main.go

@@ -2,7 +2,6 @@ package glance
 
 import (
 	"fmt"
-	"os"
 )
 
 func Main() int {
@@ -13,18 +12,9 @@ func Main() int {
 		return 1
 	}
 
-	configFile, err := os.Open(options.ConfigPath)
-
+	config, err := NewConfigFromFile(options.ConfigPath)
 	if err != nil {
-		fmt.Printf("failed opening config file: %v\n", err)
-		return 1
-	}
-
-	config, err := NewConfigFromYml(configFile)
-	configFile.Close()
-
-	if err != nil {
-		fmt.Printf("failed parsing config file: %v\n", err)
+		fmt.Printf("failed loading config file: %v\n", err)
 		return 1
 	}
 
@@ -36,6 +26,11 @@ func Main() int {
 			return 1
 		}
 
+		watchConfigFile(options.ConfigPath, func(config *Config) error {
+			app.Reload(config)
+			return nil
+		})
+
 		if app.Serve() != nil {
 			fmt.Printf("http server error: %v\n", err)
 			return 1