Skip to content

Commit

Permalink
Add example for custom hot-reloader.
Browse files Browse the repository at this point in the history
Signed-off-by: Vaibhav <[email protected]>
  • Loading branch information
vrongmeal committed May 7, 2020
1 parent 25d9447 commit b76cf1e
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 19 deletions.
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ directories can be excluded.
1. [Usage](#usage)
1. [Command line help](#command-line-help)
1. [Configuration file](#configuration-file)
1. [Custom hot reloader](#custom-hot-reloader)

## Installation

Expand Down Expand Up @@ -46,7 +47,7 @@ The following command will download and build Leaf in your
## Usage

```
$ leaf -x 'make build' -x 'make run'
leaf -x 'make build' -x 'make run'
```

The above command runs `make build` and `make run` commands
Expand Down Expand Up @@ -137,6 +138,82 @@ as such:
-f '+ go.*' -f '+ *.go' -f '+ cmd/'
```

## Custom hot reloader

The package [github.com/vrongmeal/leaf](https://pkg.go.dev/github.com/vrongmeal/leaf)
comes with utilities that can aid in creating a hot-reloader
with a simple go program.

Let's look at an example where the watcher watches the `src/`
directory for changes and for any changes builds the project.

```go
package main

import (
"fmt"
"log"
"os"
"path/filepath"

"github.com/vrongmeal/leaf"
)

func main() {
// Use a context that cancels when program is interrupted.
ctx := leaf.NewCmdContext(func(os.Signal) {
log.Println("Shutting down.")
})

cwd, err := os.Getwd()
if err != nil {
log.Fatalln(err)
}

// Root is <cwd>/src
root := filepath.Join(cwd, "src")

// Exclude "src/index.html" from results.
filters := []leaf.Filter{
{Include: false, Pattern: "src/index.html"},
}

filterCollection := leaf.NewFilterCollection(
filters,
// Matches directory or filepath.Match expressions
leaf.StandardFilterMatcher,
// Definitely excludes and shows only includes (if any)
leaf.StandardFilterHandler)

watcher, err := leaf.NewWatcher(
root,
// Standard paths to exclude, like vendor, .git,
// node_modules, venv etc.
leaf.DefaultExcludePaths,
filterCollection)
if err != nil {
log.Fatalln(err)
}

cmd, err := leaf.NewCommand("npm run build")
if err != nil {
log.Fatalln(err)
}

log.Printf("Watching: %s\n", root)

for change := range watcher.Watch(ctx) {
if change.Err != nil {
log.Printf("ERROR: %v", change.Err)
continue
}
// If no error run the command
fmt.Printf("Running: %s\n", cmd.String())
cmd.Execute(ctx)
}
}
```

---

Made with **khoon**, **paseena** and **love** `:-)` by
Expand Down
19 changes: 1 addition & 18 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"context"
"fmt"
"os"
"os/signal"
"time"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -201,26 +200,10 @@ func setupConfig() (fileErr, readErr error) {
return confFileErr, nil
}

// newCmdContext returns a context which cancels on an OS
// interrupt, i.e., cancels when process is killed.
func newCmdContext(onInterrupt func(os.Signal)) context.Context {
interrupt := make(chan os.Signal)
signal.Notify(interrupt, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())

go func(onSignal func(os.Signal), cancelProcess context.CancelFunc) {
sig := <-interrupt
onSignal(sig)
cancelProcess()
}(onInterrupt, cancel)

return ctx
}

// runEngine runs the watcher and executes the commands from
// the config on file change.
func runEngine(conf *leaf.Config) error {
ctx := newCmdContext(func(s os.Signal) {
ctx := leaf.NewCmdContext(func(s os.Signal) {
log.Infof("closing: signal received: %s", s.String())
})

Expand Down
89 changes: 89 additions & 0 deletions leaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,84 @@
// CLI tool. It includes watcher, filters and commander which
// watch files for changes, filter out required results and
// execute external commands respectively.
//
// The package comes with utilities that can aid in creating
// a hot-reloader with a simple go program.
//
// Let's look at an example where the watcher watches the `src/`
// directory for changes and for any changes builds the project.
//
// package main
//
// import (
// "log"
// "os"
// "path/filepath"
//
// "github.com/vrongmeal/leaf"
// )
//
// func main() {
// // Use a context that cancels when program is interrupted.
// ctx := leaf.NewCmdContext(func(os.Signal) {
// log.Println("Shutting down.")
// })
//
// cwd, err := os.Getwd()
// if err != nil {
// log.Fatalln(err)
// }
//
// // Root is <cwd>/src
// root := filepath.Join(cwd, "src")
//
// // Exclude "src/index.html" from results.
// filters := []leaf.Filter{
// {Include: false, Pattern: "src/index.html"},
// }
//
// filterCollection := leaf.NewFilterCollection(
// filters,
// // Matches directory or filepath.Match expressions
// leaf.StandardFilterMatcher,
// // Definitely excludes and shows only includes (if any)
// leaf.StandardFilterHandler)
//
// watcher, err := leaf.NewWatcher(
// root,
// // Standard paths to exclude, like vendor, .git,
// // node_modules, venv etc.
// leaf.DefaultExcludePaths,
// filterCollection)
// if err != nil {
// log.Fatalln(err)
// }
//
// cmd, err := leaf.NewCommand("npm run build")
// if err != nil {
// log.Fatalln(err)
// }
//
// log.Printf("Watching: %s\n", root)
//
// for change := range watcher.Watch(ctx) {
// if change.Err != nil {
// log.Printf("ERROR: %v", change.Err)
// continue
// }
// // If no error run the command
// log.Printf("Running: %s\n", cmd.String())
// cmd.Execute(ctx)
// }
// }
//
package leaf

import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"time"
Expand Down Expand Up @@ -65,6 +138,22 @@ type Config struct {
Delay time.Duration `mapstructure:"delay"`
}

// NewCmdContext returns a context which cancels on an OS
// interrupt, i.e., cancels when process is killed.
func NewCmdContext(onInterrupt func(os.Signal)) context.Context {
interrupt := make(chan os.Signal)
signal.Notify(interrupt, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())

go func(onSignal func(os.Signal), cancelProcess context.CancelFunc) {
sig := <-interrupt
onSignal(sig)
cancelProcess()
}(onInterrupt, cancel)

return ctx
}

// GoModuleInfo returns the go module information which
// includes the build info (version etc.).
func GoModuleInfo() (*debug.Module, error) {
Expand Down

0 comments on commit b76cf1e

Please sign in to comment.