Thanks to visit codestin.com
Credit goes to github.com

Skip to content

PylotLight/refresh

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Refresh Hot Reload

Refresh 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.

While refresh was built for reloading codebases it can be used to execute terminal commands based on file system changes

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 refresh 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 .tar.gz files per platform are available via github releases

go install github.com/atterpac/refresh/cmd/refesh@latest

Alternative if you wish to use as a package and not a cli

go get github.com/atterpac/refresh

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

refresh -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 refresh provides a function NewEngineFromOptions which takes an refresh.Config and allows for the engine.Start() function

Using refresh as a library also opens the ability to add a 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
     refresh "github.com/atterpac/refresh/engine"
    )

func main () {
	ignore := refresh.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 := refresh.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 := refresh.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 refresh 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 refresh.EventHandle

refresh.EventContinue continues with the reload process as normal and follows the hotato ruleset defined in the config

refresh.EventBypass disregards all config rulesets and restarts the exec process

refresh.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    // Relative path based on root if root is ./myProject paths start with "myProject/..."
}
// Available returns from the Callback function
const (
	EventContinue EventHandle = iota
	EventBypass
	EventIgnore
)

func ExampleCallback(e refresh.EventCallback) refresh.EventHandle {
	switch e.Type {
	case refresh.Create:
		// Continue with reload process based on configured ruleset
		return refresh.EventContinue
	case refresh.Write:
		// Ignore a file that would normally trigger a reload based on config
		if e.Path == "./path/to/watched/file" {
			return refresh.EventIgnore
		}
		// Continue with reload ruleset but add some extra logs/logic
		fmt.Println("File Modified: %s", e.Path)	
		return EventContinue
	case refresh.Remove:
		// Hotato will ignore this event by default
		// Return EventBypass to force reload process
		return refresh.EventBypass
	}
	return refresh.EventContinue
}

Config File

If you would prefer to load from a config file rather than building the structs you can use

refresh.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

About

Live reload development built in golang

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 100.0%