README ¶
🚧 EARLY DEVELOPMENT 🚧
This is a small tool I have built that is largely untested off of my machine. You are welcome to try it and if you notice any issues report them on the github and I will look into them.
HOTATO Hot Reload
Hotato (hot potato) is CLI tool for hot reloading your codebase based on file system changes using notify with the ablity to use as a golang library in your own projects.
Key Features
- Based on Notify to allievate common problems with popular FS libraries on mac that open a listener per file by using apples built-in FSEvents.
- Allows for customization via code / config file / cli flags
- Extended customization when used as a library using reloadCallback to bypass hotato rulesets and add addtional logic/logging on your applications end
- Default slogger built in with the ablity to mute logs as well as pass in your own slog handler to be used in app
- MIT licensed
Install
Installing via go CLI is the easiest method more methods are on the list
go install github.com/atterpac/hotato/cmd/hotato@latest
Alternative if you wish to use as a package and not a cli
go get github.com/atterpac/hotato
Usage
Flags
-p
Root path that will be watched and commands will be executed in typically this is './'
-be
Command to be called before the exec command for example go mod tidy
-e
Command to be called when a modification is detected for example go run main.go
-ae
Command to b be called when a modifcation is detected after the main process closes
-l
Log Level to display options can include "debug", "info","warn","error"
-f
path to a TOML config file see Config File for details on the format of config
-id
Ignore directories provided as a comma-separated list
-if
Ignore files provided as a comma-separated list
-ie
Ignore extensions provided as a comma-separated list
-d
Debounce timer in milliseconds, used to ignore repetitive system
Example
hotato -p ./ -e "go run main.go" -be "go mod tidy" -ae "rm ./main" -l "debug" -id ".git, node_modules" -if ".env" -ie ".db, .sqlite" -d 500
Embedding into your dev project
There can be some uses where you might want to start a watcher internally or for a tool for development Hotato provides a function NewEngineFromOptions
which takes an hotato.Config
and allows for the engine.Start()
function
Using hotato as a library also opens the ability to add a Callback Callback function that is called on every FS notification
Structs
type Config struct {
RootPath string `toml:"root_path"`
PreExec string `toml:"pre_exec"`
ExecCommand string `toml:"exec_command"`
PostExec string `toml:"post_exec"`
Ignore Ignore `toml:"ignore"`
LogLevel string `toml:"log_level"`
Debounce int `toml:"debounce"`
Slog *slog.Logger
ExternalSlog bool
}
type Ignore struct {
Dir map[string]bool `toml:"dir"`
File map[string]bool `toml:"file"`
Extension map[string]bool `toml:"extension"`
}
Example
import ( // other imports
"github.com/atterpac/hotato/engine"
)
func main () {
ignore := hotato.Ignore{
File: map[string]bool{{"ignore.go",true},{".env", true}},
Dir: map[string]bool{{".git",true},{"node_modules", true}},
Extension: map[string]bool{{".txt",true},{".db", true}},
}
config := hotato.Config{
RootPath: "./subExecProcess",
ExecCommand: "go run main.go",
LogLevel: "info", // debug | info | warn | error | mute (discards all logs)
Ignore: ignore,
Debounce: 1000,
Slog: nil, // Optionally provide a slog interface
// if nil a default will be provided
// If provided stdout will not be piped through gotato
// Optionally provide a callback function to be called upon file notification events
Callback: func(*EventCallback) EventHandle
}
engine := hotato.NewEngineFromConfig(config)
engine.Start()
// Stop monitoring files and kill child processes
engine.Stop()
}
Reload Callback
Event Types
The following are all the file system event types that can be passed into the callback functions. Important to note that some actions only are emitted are certain OSs and you may have to handle those if you wish to bypass hotato rulesets
const (
// Base Actions
Create Event = iota
Write
Remove
Rename
// Windows Specific Actions
ActionModified
ActionRenamedNewName
ActionRenamedOldName
ActionAdded
ActionRemoved
ChangeLastWrite
ChangeAttributes
ChangeSize
ChangeDirName
ChangeFileName
ChangeSecurity
ChangeCreation
ChangeLastAccess
// Linux Specific Actions
InCloseWrite
InModify
InMovedTo
InMovedFrom
InCreate
InDelete
)
// Used as a response to the Callback
const (
EventContinue EventHandle = iota
EventBypass
EventIgnore
)
Callback Function
Below describes the data that you recieve in the callback function as well as an example of how this could be used.
Callbacks should return an hotato.EventHandle
hotato.EventContinue
continues with the reload process as normal and follows the hotato ruleset defined in the config
hotato.EventBypass
disregards all config rulesets and restarts the exec process
hotato.EventIgnore
ignores the event and continues monitoring
// Called whenever a change is detected in the filesystem
// By default we ignore file rename/remove and a bunch of other events that would likely cause breaking changes on a reload see eventmap_[oos].go for default rules
type EventCallback struct {
Type Event // Type of Notification (Write/Create/Remove...)
Time time.Time // time.Now() when event was triggered
Path string // Full path to the modified file
}
// Available returns from the Callback function
const (
EventContinue EventHandle = iota
EventBypass
EventIgnore
)
func ExampleCallback(e hotato.EventCallback) hotato.EventHandle {
switch e.Type {
case hotato.Create:
// Continue with reload process based on configured ruleset
return hotato.EventContinue
case hotato.Write:
// Ignore a file that would normally trigger a reload based on config
if e.Path == "./path/to/watched/file" {
return hotato.EventIgnore
}
// Continue with reload ruleset but add some extra logs/logic
fmt.Println("File Modified: %s", e.Path)
return EventContinue
case hotato.Remove:
// Hotato will ignore this event by default
// Return EventBypass to force reload process
return hotato.EventBypass
}
return hotato.EventContinue
}
Config File
If you would prefer to load from a config file rather than building the structs you can use
hotato.NewEngineFromTOML("path/to/toml")
Example Config
[config]
# Relative to this files location
root_path = "./"
# Runs prior to the exec command starting
pre_exec = "go mod tidy"
# Command to run on reload
exec_command = "go run main.go"
# Runs when a file reload is triggered after killing the previous process
post_exec = ""
# debug | info | warn | error | mute
# Defaults to Info if not provided
log_level = "info"
# Debounce setting for ignoring reptitive file system notifications
debounce = 1000 # Milliseconds
# Sets what files the watcher should ignore
[config.ignore]
# Directories to ignore
dir = [".git", "node_modules", "newdir"]
# Files to ignore
file = [".DS_Store", ".gitignore", ".gitkeep", "newfile.go"]
# File extensions to ignore
extension = [".db", ".sqlite"]
Alternatives
Hotato not for you? Here are some popular hot reload alternatives
Documentation ¶
There is no documentation for this package.