feat(config): Add exclude field#132
Conversation
Add top-level exclude for suppressing services from a profile and fuku run will silently drop them at startup
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #132 +/- ##
==========================================
+ Coverage 89.31% 89.52% +0.20%
==========================================
Files 63 63
Lines 5823 5899 +76
==========================================
+ Hits 5201 5281 +80
+ Misses 489 486 -3
+ Partials 133 132 -1
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a top-level exclude configuration field to let fuku run suppress specific services at startup (by filtering them out during discovery), along with normalization and test coverage to ensure exclude is parsed, merged, and deduplicated consistently. The PR also refactors the TUI run loop to coordinate runner/UI shutdown more cleanly and re-enables log output after the UI exits.
Changes:
- Add
Config.Excludeplus config normalization (Normalize()/normalizeExclude) and loader/merge coverage. - Filter excluded services during discovery resolution so excluded names are dropped before ordering/validation.
- Update TUI wiring to inject
render.Writerand run UI/runner concurrently with coordinated shutdown and error propagation.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| internal/config/validate.go | Adds profile validation to ensure referenced services exist (new behavior). |
| internal/config/validate_test.go | Adds tests for profile reference validation scenarios. |
| internal/config/merge_test.go | Adds YAML merge test ensuring exclude lists concatenate. |
| internal/config/loader.go | Runs cfg.Normalize() post-unmarshal (tiers + exclude). |
| internal/config/loader_test.go | Updates profile YAML shape in test and adds exclude parsing/override tests. |
| internal/config/config.go | Introduces Exclude field and normalization pipeline. |
| internal/config/config_test.go | Adds unit tests for normalizeExclude and Normalize(). |
| internal/app/errors/errors.go | Adds ErrProfileReferenceUndefined. |
| internal/app/discovery/discovery.go | Filters out excluded services during Resolve. |
| internal/app/discovery/discovery_test.go | Adds coverage for exclude behavior during discovery. |
| internal/app/cli/tui.go | Injects render.Writer; runs UI and runner concurrently and restores output on exit. |
| internal/app/cli/tui_test.go | Updates TUI tests for writer injection and new run-loop behavior. |
| cmd/main.go | Supplies writer via FX so it can be injected into TUI. |
| fuku.yaml | Adds example worker service. |
| fuku.override.yaml | Demonstrates excluding worker. |
| examples/bookstore/worker/src/main.go | Adds new example service implementation. |
| examples/bookstore/worker/Makefile | Adds make target to run the new example worker. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 29 out of 30 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
internal/config/config_test.go:233
- The
cfg := DefaultConfig()is shared across subtests and mutated in each case, which couples tests and can cause state leakage (and future flakes if subtests are parallelized or more fields are added). Create a fresh config inside eacht.Run(or copy the config) so cases are isolated.
func Test_ServerListen(t *testing.T) {
cfg := DefaultConfig()
tests := []struct {
name string
listen string
want string
}{
{
name: "configured address",
listen: "127.0.0.1:9876",
want: "127.0.0.1:9876",
},
{
name: "empty returns empty",
listen: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg.Server.Listen = tt.listen
assert.Equal(t, tt.want, cfg.ServerListen())
})
}
internal/config/config_test.go:262
- Same issue here:
cfg := DefaultConfig()is shared and mutated across subtests. Instantiate a new config per test case to keep subtests independent and avoid hidden coupling.
func Test_ServerToken(t *testing.T) {
cfg := DefaultConfig()
tests := []struct {
name string
token string
want string
}{
{
name: "configured token",
token: "my-secret",
want: "my-secret",
},
{
name: "empty returns empty",
token: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg.Server.Auth.Token = tt.token
assert.Equal(t, tt.want, cfg.ServerToken())
})
}
Add top-level
excludefor suppressing services from a profile andfuku runwill silently drop them at startup