mirror of
https://github.com/anotherhadi/spilltea.git
synced 2026-07-06 12:42:32 +02:00
Per-project configuration overwrites
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Project Management](#project-management)
|
- [Project Management](#project-management)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
|
- [Per-project configuration](#per-project-configuration)
|
||||||
- [CLI Flags](#cli-flags)
|
- [CLI Flags](#cli-flags)
|
||||||
- [Plugin System](#plugin-system)
|
- [Plugin System](#plugin-system)
|
||||||
- [Vim / Neovim Integration](#vim--neovim-integration)
|
- [Vim / Neovim Integration](#vim--neovim-integration)
|
||||||
@@ -118,6 +119,18 @@ Check the default configuration with all the options [here](./internal/config/de
|
|||||||
|
|
||||||
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
|
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
|
||||||
|
|
||||||
|
### Per-project configuration
|
||||||
|
|
||||||
|
You can override any config value on a per-project basis by placing a `config.yaml` file inside the project directory (e.g. `~/.local/share/spilltea/projects/my-project/config.yaml`).
|
||||||
|
|
||||||
|
Only the keys present in that file are overridden; everything else falls back to the global config.
|
||||||
|
|
||||||
|
The priority order is:
|
||||||
|
|
||||||
|
1. Global config (`~/.config/spilltea/config.yaml`)
|
||||||
|
2. Project config (`<project-dir>/config.yaml`)
|
||||||
|
3. CLI flags (always win)
|
||||||
|
|
||||||
## CLI Flags
|
## CLI Flags
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+24
-14
@@ -99,21 +99,26 @@ func main() {
|
|||||||
}
|
}
|
||||||
config.Global.Version = version
|
config.Global.Version = version
|
||||||
|
|
||||||
if flagPluginsDir != "" {
|
sslInsecureChanged := cmd.Flags().Changed("ssl-insecure")
|
||||||
config.Global.App.PluginsDir = flagPluginsDir
|
applyCLI := func(c *config.Config) {
|
||||||
}
|
if flagPluginsDir != "" {
|
||||||
if flagHost != "" {
|
c.App.PluginsDir = flagPluginsDir
|
||||||
config.Global.App.Host = flagHost
|
}
|
||||||
}
|
if flagHost != "" {
|
||||||
if flagPort != 0 {
|
c.App.Host = flagHost
|
||||||
config.Global.App.Port = flagPort
|
}
|
||||||
}
|
if flagPort != 0 {
|
||||||
if flagUpstreamProxy != "" {
|
c.App.Port = flagPort
|
||||||
config.Global.App.UpstreamProxy = flagUpstreamProxy
|
}
|
||||||
}
|
if flagUpstreamProxy != "" {
|
||||||
if cmd.Flags().Changed("ssl-insecure") {
|
c.App.UpstreamProxy = flagUpstreamProxy
|
||||||
config.Global.App.SslInsecure = flagSslInsecure
|
}
|
||||||
|
if sslInsecureChanged {
|
||||||
|
c.App.SslInsecure = flagSslInsecure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
applyCLI(config.Global)
|
||||||
|
config.SetCLIOverrides(applyCLI)
|
||||||
|
|
||||||
style.Init()
|
style.Init()
|
||||||
icons.Init(config.Global)
|
icons.Init(config.Global)
|
||||||
@@ -126,6 +131,11 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("project: %w", err)
|
return fmt.Errorf("project: %w", err)
|
||||||
}
|
}
|
||||||
|
if err := config.MergeProjectConfig(filepath.Dir(project.Path)); err != nil {
|
||||||
|
return fmt.Errorf("project config: %w", err)
|
||||||
|
}
|
||||||
|
icons.Init(config.Global)
|
||||||
|
keys.Init(config.Global)
|
||||||
broker := intercept.NewBroker()
|
broker := intercept.NewBroker()
|
||||||
m := appUI.New(broker, project.Name, project.Path)
|
m := appUI.New(broker, project.Name, project.Path)
|
||||||
final, err := tea.NewProgram(m).Run()
|
final, err := tea.NewProgram(m).Run()
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
tea "charm.land/bubbletea/v2"
|
tea "charm.land/bubbletea/v2"
|
||||||
|
"github.com/anotherhadi/spilltea/internal/config"
|
||||||
|
"github.com/anotherhadi/spilltea/internal/icons"
|
||||||
"github.com/anotherhadi/spilltea/internal/intercept"
|
"github.com/anotherhadi/spilltea/internal/intercept"
|
||||||
|
"github.com/anotherhadi/spilltea/internal/keys"
|
||||||
appUI "github.com/anotherhadi/spilltea/internal/ui/app"
|
appUI "github.com/anotherhadi/spilltea/internal/ui/app"
|
||||||
homeUI "github.com/anotherhadi/spilltea/internal/ui/home"
|
homeUI "github.com/anotherhadi/spilltea/internal/ui/home"
|
||||||
)
|
)
|
||||||
@@ -34,6 +40,11 @@ func (m rootModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
if m.state == rootStateHome {
|
if m.state == rootStateHome {
|
||||||
if sel, ok := msg.(homeUI.ProjectSelectedMsg); ok {
|
if sel, ok := msg.(homeUI.ProjectSelectedMsg); ok {
|
||||||
|
if err := config.MergeProjectConfig(filepath.Dir(sel.Project.Path)); err != nil {
|
||||||
|
log.Printf("project config: %v", err)
|
||||||
|
}
|
||||||
|
icons.Init(config.Global)
|
||||||
|
keys.Init(config.Global)
|
||||||
broker := intercept.NewBroker()
|
broker := intercept.NewBroker()
|
||||||
app := appUI.New(broker, sel.Project.Name, sel.Project.Path)
|
app := appUI.New(broker, sel.Project.Name, sel.Project.Path)
|
||||||
m.app = app
|
m.app = app
|
||||||
|
|||||||
@@ -15,6 +15,18 @@ Check the default configuration with all the options [here](./internal/config/de
|
|||||||
|
|
||||||
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
|
Colors and styles can be customized using [ilovetui](https://github.com/anotherhadi/ilovetui), which applies theme changes across all compatible TUI applications at once.
|
||||||
|
|
||||||
|
### Per-project configuration
|
||||||
|
|
||||||
|
You can override any config value on a per-project basis by placing a `config.yaml` file inside the project directory (e.g. `~/.local/share/spilltea/projects/my-project/config.yaml`).
|
||||||
|
|
||||||
|
Only the keys present in that file are overridden; everything else falls back to the global config.
|
||||||
|
|
||||||
|
The priority order is:
|
||||||
|
|
||||||
|
1. Global config (`~/.config/spilltea/config.yaml`)
|
||||||
|
2. Project config (`<project-dir>/config.yaml`)
|
||||||
|
3. CLI flags (always win)
|
||||||
|
|
||||||
## CLI Flags
|
## CLI Flags
|
||||||
|
|
||||||
<!-- exec: echo '```' && go run ./cmd/spilltea -h && echo '```' -->
|
<!-- exec: echo '```' && go run ./cmd/spilltea -h && echo '```' -->
|
||||||
|
|||||||
@@ -65,6 +65,43 @@ type GlobalPlugin struct {
|
|||||||
|
|
||||||
var Global *Config
|
var Global *Config
|
||||||
|
|
||||||
|
var projectCLIOverrides func(*Config)
|
||||||
|
|
||||||
|
// SetCLIOverrides stores a function that re-applies CLI flag overrides.
|
||||||
|
// It is called automatically after MergeProjectConfig so that CLI flags
|
||||||
|
// always win over both global and per-project config.
|
||||||
|
func SetCLIOverrides(fn func(*Config)) {
|
||||||
|
projectCLIOverrides = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeProjectConfig merges <projectDir>/config.yaml on top of the current
|
||||||
|
// Global config, then re-applies any registered CLI overrides.
|
||||||
|
// If no config.yaml exists in the project directory, this is a no-op.
|
||||||
|
func MergeProjectConfig(projectDir string) error {
|
||||||
|
projectConfigPath := filepath.Join(projectDir, "config.yaml")
|
||||||
|
if _, err := os.Stat(projectConfigPath); errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := viper.New()
|
||||||
|
pv.SetConfigFile(projectConfigPath)
|
||||||
|
if err := pv.ReadInConfig(); err != nil {
|
||||||
|
return fmt.Errorf("project config: %w", err)
|
||||||
|
}
|
||||||
|
if err := viper.MergeConfigMap(pv.AllSettings()); err != nil {
|
||||||
|
return fmt.Errorf("merge project config: %w", err)
|
||||||
|
}
|
||||||
|
if err := viper.Unmarshal(Global); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Global.App.ProxyAuth = expandEnvValue(Global.App.ProxyAuth)
|
||||||
|
Global.App.UpstreamProxy = expandEnvValue(Global.App.UpstreamProxy)
|
||||||
|
if projectCLIOverrides != nil {
|
||||||
|
projectCLIOverrides(Global)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Load(path string) error {
|
func Load(path string) error {
|
||||||
var defaults map[string]any
|
var defaults map[string]any
|
||||||
if err := yaml.Unmarshal(defaultConfig, &defaults); err != nil {
|
if err := yaml.Unmarshal(defaultConfig, &defaults); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user