Skip to content

Commit 12aa19b

Browse files
authored
Merge pull request #21 from atterpac/refactor/engine-redesign
Refactor/engine redesign
2 parents 3049c4d + 0165901 commit 12aa19b

64 files changed

Lines changed: 2209 additions & 1569 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@ jobs:
88

99
steps:
1010
- name: Checkout code
11-
uses: actions/checkout@v2
11+
uses: actions/checkout@v4
1212

1313
- name: Set up Go
14-
uses: actions/setup-go@v4
14+
uses: actions/setup-go@v5
1515
with:
16-
go-version: '>=1.21.0'
17-
id: go
16+
go-version: '1.25.x'
1817

19-
- name: Tidy
20-
run: go mod tidy
18+
- name: Vet
19+
run: go vet ./...
2120

2221
- name: Test
23-
run: go test -v ./engine
22+
run: go test -race ./...
2423

2524
build:
2625
runs-on: ${{ matrix.os }}
@@ -31,21 +30,18 @@ jobs:
3130

3231
steps:
3332
- name: Checkout code
34-
uses: actions/checkout@v2
33+
uses: actions/checkout@v4
3534

3635
- name: Set up Go
37-
uses: actions/setup-go@v4
36+
uses: actions/setup-go@v5
3837
with:
39-
go-version: '>=1.21.0'
40-
41-
- name: Dependencies
42-
run: go mod tidy
38+
go-version: '1.25.x'
4339

4440
- name: Build application
4541
run: go build ./cmd/refresh/
4642

4743
- name: Upload artifact
48-
uses: actions/upload-artifact@v2
44+
uses: actions/upload-artifact@v4
4945
with:
50-
name: refresh
51-
path: ./refresh
46+
name: refresh-${{ matrix.os }}
47+
path: ./refresh*

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
21
dist/
2+
3+
# Build artifacts — never commit compiled binaries
4+
/app
5+
bin/
6+
*.exe
7+
8+
# Example runtime output
9+
examples/kitchen-sink/artifacts/
10+
examples/kitchen-sink/sub/marker.txt

README.md

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,36 @@ These declarations let refresh know when you would like to kill the stale proces
3939
Whatever command after REFRESH is considered your "main" subprocess and the one that is tracked inside of refresh
4040

4141
## Embedding into your dev project
42-
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 `engine.Config` and allows for the `engine.Start()` function
42+
There can be some uses where you might want to start a watcher internally or for a tool for development refresh provides `NewEngineFromConfig`, which takes an `engine.Config` and returns an engine you can `Start()`.
4343

4444
Using refresh as a library also opens the ability to add a [Callback](https://github.com/atterpac/refresh#reload-callback) function that is called on every FS notification
4545

4646
### Structs
4747
```go
4848
type Config struct {
49-
RootPath string `toml:"root_path"`
50-
BackgroundExec string `toml:"background_exec"` // Execute that stays running and is unaffected by any reloads npm run dev for example
51-
BackgroundCheck bool `toml:"background_check"`
52-
Ignore Ignore `toml:"ignore"`
53-
ExecList []string `toml:"exec_list"` // See [Execute Lifecycle](https://github.com/atterpac/refresh#execute-lifecycle)
54-
LogLevel string `toml:"log_level"`
55-
Debounce int `toml:"debounce"`
56-
Callback func(*EventCallback) EventHandle
57-
Slog *slog.Logger
49+
RootPath string `toml:"root_path" yaml:"root_path"`
50+
BackgroundStruct process.Execute `toml:"background" yaml:"background"` // Execute that stays running and is unaffected by reloads (e.g. npm run dev)
51+
Ignore Ignore `toml:"ignore" yaml:"ignore"`
52+
ExecStruct []process.Execute `toml:"executes" yaml:"executes"` // Preferred: typed executes, see [Execute Lifecycle]
53+
ExecList []string `toml:"exec_list" yaml:"exec_list"` // Simpler form, see [Execute Lifecycle]
54+
LogLevel string `toml:"log_level" yaml:"log_level"`
55+
Debounce int `toml:"debounce" yaml:"debounce"`
56+
Callback func(*EventCallback) EventHandle
57+
Slog *slog.Logger
5858
}
5959

6060
type Ignore struct {
61-
Dir []string `toml:"dir"` // Specfic directory to ignore ie; node_modules
62-
File []string `toml:"file"` // Specific file to ignore
63-
WatchExten []string `toml:"extension"` // Extensions to watch NOT ignore, ie; `*.go, *.js` would ignore any file that is not go or javascript
64-
GitIgnore bool `toml:"git_ignore"` // When true will check for a .gitignore in the root directory and add all entries to the ignore
61+
Dir []string `toml:"dir" yaml:"dir"` // Directories to ignore, e.g. node_modules
62+
File []string `toml:"file" yaml:"file"` // Files to ignore
63+
WatchedExten []string `toml:"watched_extension" yaml:"watched_extension"` // Extensions to watch; anything else is ignored
64+
IgnoreGit bool `toml:"git" yaml:"git"` // When true, .gitignore entries in the root are also ignored
6565
}
6666

6767
type Execute struct {
68-
Cmd string `toml:"cmd" yaml:"cmd"` // Execute command
69-
ChangeDir string `toml:"dir" yaml:"dir"` // If directory needs to be changed to call this command relative to the root path
70-
IsBlocking bool `toml:"blocking" yaml:"blocking"` // Should the following executes wait for this one to complete
71-
IsPrimary bool `toml:"primary" yaml:"primary"` // Only one primary command can be run at a time
72-
DelayNext int `toml:"delay_next" yaml:"delay_next"` // Delay in milliseconds before running command
68+
Cmd string `toml:"cmd" yaml:"cmd"` // Command to run
69+
ChangeDir string `toml:"dir" yaml:"dir"` // Directory to run in, relative to root_path
70+
DelayNext int `toml:"delay_next" yaml:"delay_next"` // Delay in milliseconds before running
71+
Type ExecuteType `toml:"type" yaml:"type"` // background | once | blocking | primary
7372
}
7473
```
7574

@@ -90,45 +89,36 @@ func main () {
9089
Dir: []string{".git","*/node_modules", "!api/*"}, // Ignore .git and any node_modules in the directory or anything not within the api directory
9190
IgnoreGit: true, // .gitignore sitting in the root directory? set this to true to automatially ignore those files
9291
}
93-
// Build execute structs
92+
// Build execute structs. Type is one of: background | once | blocking | primary
9493
tidy := engine.Execute{
95-
Cmd: "go mod tidy",
96-
IsBlocking: true, // Next command should wait for this to finish
94+
Cmd: "go mod tidy",
95+
Type: engine.Blocking, // Next command waits for this to finish
9796
}
9897
build := engine.Execute{
99-
Cmd: "go build -o ./bin/myapp",
100-
IsBlocking: true, // Wait to kill (next step) until the new binary is built
98+
Cmd: "go build -o ./bin/myapp",
99+
Type: engine.Blocking, // Block until the new binary is built before restarting
101100
}
102-
// Provided KILL_STALE will tell refresh when you would like to remove the stale process to prepare to launch the new one
103-
kill := engine.KILL_STALE
104-
// Primary process usually runs your binary
101+
// Primary process usually runs your binary; it is killed and restarted on each reload.
105102
run := engine.Execute{
106-
ChangeDir: "./bin", // Change directory to call command in
107-
Cmd: "./myapp",
108-
IsBlocking: false, // Should not block because it doesnt finish until Killed by refresh
109-
IsPrimary: true, // This is the main process refersh is rerunning so denoting it as primary
103+
ChangeDir: "./bin", // Directory to run the command in (relative to root_path)
104+
Cmd: "./myapp",
105+
Type: engine.Primary,
110106
}
111-
// Create config to pass into refresh.NewEngineFromConfig()
107+
// Create config to pass into engine.NewEngineFromConfig()
112108
config := engine.Config{
113-
RootPath: "./test",
114-
// Below is ran when a reload is triggered before killing the stale version
109+
RootPath: "./test",
115110
Ignore: ignore,
116-
Debounce: 1000, // Time in ms to ignore repitive reload triggers usually caused by an OS creating multiple write/rename events for a singular change
117-
LogLevel: "debug", // debug | info | warn | error | mute -> surpresses all logs to the stdOut
118-
Callback: RefreshCallback, // func(*engine.Callback) refresh.EventHandle {}
119-
ExecStruct: []refresh.Execute{tidy, build, kill, run},
120-
// Alternatively for easier config but less control over executes
121-
// ExecList: []string{"go mod tidy", "go build -o ./myapp", refresh.KILL_EXEC, refresh.REFRESH_EXEC, "./myapp"}
122-
// All calls will be blocking with the exception of the call after REFRESH
123-
// Both KILL_EXEC and REFRESH_EXEC are **REQUIRED** for refresh to function properly
124-
// engine.KILL_EXEC denotes when the stale process should be killed
125-
// engine.REFRESH_EXEC denotes the next execute is "primary"
126-
Slog: nil, // Optionally provide a slog interface
127-
// if nil a default will be provided
128-
// If provided stdout will not be piped through refresh
111+
Debounce: 1000, // Time in ms to coalesce repetitive reload triggers (the last save in a burst wins)
112+
LogLevel: "debug", // debug | info | warn | error | mute
113+
Callback: RefreshCallback, // func(*engine.EventCallback) engine.EventHandle
114+
ExecStruct: []engine.Execute{tidy, build, run},
115+
// Alternatively, the simpler ExecList form. REFRESH_EXEC marks the command
116+
// after it as the primary process; everything else runs blocking in order.
117+
// ExecList: []string{"go mod tidy", "go build -o ./myapp", engine.REFRESH_EXEC, "./myapp"}
118+
Slog: nil, // Optionally provide your own *slog.Logger; a default is used if nil
129119
}
130120

131-
engine, err := refresh.NewEngineFromConfig(config)
121+
engine, err := engine.NewEngineFromConfig(config)
132122
if err != nil {
133123
//Handle err
134124
}
@@ -248,12 +238,28 @@ func ExampleCallback(e refresh.EventCallback) refresh.EventHandle {
248238
return engine.EventContinue
249239
}
250240
```
241+
### Logging
242+
243+
Refresh ships with a built-in structured logger. The level is set via the
244+
`log_level` config field (`debug | info | warn | error | mute`) and can also be
245+
controlled at runtime — these are safe to call from any goroutine:
246+
247+
```go
248+
engine.SetLogLevel("debug") // change verbosity live ("mute" suppresses output)
249+
engine.DisableLogs() // mute without losing the configured level
250+
engine.EnableLogs() // resume at the previous level
251+
engine.SetLogger(myLogger) // supply your own *slog.Logger (still controllable)
252+
```
253+
254+
`DisableLogs`/`EnableLogs` toggle a single switch shared by the whole logger, so
255+
re-enabling restores the previously configured level. Subprocess stdout/stderr
256+
is written straight to the terminal and is not affected by these controls.
257+
251258
### Config File
252259

253260
If you would prefer to load from a [config](https://github.com/Atterpac/refresh#config-file) file rather than building the structs you can use
254261
```go
255262
engine.NewEngineFromTOML("path/to/toml")
256-
engine.SetLogger(//Input slog.Logger)
257263
```
258264
#### Example Config
259265
```toml
@@ -262,11 +268,10 @@ engine.SetLogger(//Input slog.Logger)
262268
root_path = "./"
263269
# debug | info(default) | warn | error | mute
264270
log_level = "info"
265-
# Debounce setting for ignoring reptitive file system notifications
271+
# Debounce setting for coalescing repetitive file system notifications
266272
debounce = 1000 # Milliseconds
267-
# Sets what files the watcher should ignore
268-
background_check = true
269273

274+
# Sets what files the watcher should ignore
270275
[config.ignore]
271276
# Ignore follows normal pattern matching including /**/
272277
# Directories to ignore
@@ -308,15 +313,6 @@ cmd="./app"
308313
primary=true
309314
```
310315

311-
### Background Check Callback
312-
There are instances where you want to wait for the "build" steps for something like vite or a server connection that could take a varying amount
313-
of time to reach a ready state. Refresh adds `engine.AttachBackgroundCallback()` which will hault the execute commands until the callback returns
314-
true (or false for error and shutting down). This could be used along side a ping to the vite port for example to ensure it is reached before
315-
running commands that rely on it. This requires 2 things
316-
317-
- A callback function that is `func() bool` and returns true when ready and false when errored or exited
318-
- Attaching the callback via `engine.AttachBackgroundCallback()` prior to running `engine.Start()`
319-
320316
#### Flags
321317
This method is possible but not the most verbose and controlled way to use refresh
322318

app

-5 MB
Binary file not shown.

0 commit comments

Comments
 (0)