@@ -156,6 +156,7 @@ iris config show
|
||||
[core]
|
||||
version = 1
|
||||
shell = "" # "zsh", "bash", "fish", or empty for auto-detection
|
||||
shell-login = false # run the selected shell as a login shell; can also be enabled with iris --shell-login
|
||||
mode = "last" # "last", "spec", or "history"
|
||||
debug = false
|
||||
expand-alias = true
|
||||
|
||||
@@ -35,6 +35,7 @@ func (d Duration) MarshalText() ([]byte, error) {
|
||||
type CoreConfig struct {
|
||||
Version int `toml:"version"`
|
||||
Shell string `toml:"shell"`
|
||||
ShellLogin bool `toml:"shell-login"`
|
||||
Mode string `toml:"mode"`
|
||||
Debug bool `toml:"debug"`
|
||||
ExpandAlias bool `toml:"expand-alias"`
|
||||
|
||||
@@ -12,6 +12,9 @@ func TestDefaultConfigAndState(t *testing.T) {
|
||||
if cfg.Core.Version != 1 {
|
||||
t.Errorf("expected version 1, got %d", cfg.Core.Version)
|
||||
}
|
||||
if cfg.Core.ShellLogin {
|
||||
t.Errorf("expected login shell to be disabled by default")
|
||||
}
|
||||
if cfg.UI.MaxSuggestions != 100 {
|
||||
t.Errorf("expected suggestions 100, got %d", cfg.UI.MaxSuggestions)
|
||||
}
|
||||
@@ -90,14 +93,23 @@ func TestValidationAndEnvironmentOverrides(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// UserConfigDir follows HOME on macOS and XDG_CONFIG_HOME on Unix.
|
||||
// Override both so the test cannot read or write the user's real config.
|
||||
t.Setenv("HOME", tmpDir)
|
||||
t.Setenv("XDG_CONFIG_HOME", tmpDir)
|
||||
|
||||
configDir := filepath.Join(tmpDir, "iris")
|
||||
configPath, err := ConfigPath()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get config path: %v", err)
|
||||
}
|
||||
configDir := filepath.Dir(configPath)
|
||||
if mkErr := os.MkdirAll(configDir, 0755); mkErr != nil {
|
||||
t.Fatalf("failed to create config dir: %v", mkErr)
|
||||
}
|
||||
configPath := filepath.Join(configDir, "config.toml")
|
||||
tomlContent := `
|
||||
[core]
|
||||
shell-login = true
|
||||
|
||||
[ai]
|
||||
enabled = true
|
||||
provider = "groq"
|
||||
@@ -132,6 +144,9 @@ model = "qwen-2.5-coder-32b"
|
||||
if !cfg.Core.Debug {
|
||||
t.Errorf("expected debug to be true")
|
||||
}
|
||||
if !cfg.Core.ShellLogin {
|
||||
t.Errorf("expected login shell to be enabled from TOML")
|
||||
}
|
||||
if cfg.Core.Shell != "fish" {
|
||||
t.Errorf("expected shell fish, got %q", cfg.Core.Shell)
|
||||
}
|
||||
@@ -178,6 +193,9 @@ func TestLoadSave(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// UserConfigDir follows HOME on macOS and XDG_CONFIG_HOME on Unix.
|
||||
// Override both so the test cannot read or write the user's real config.
|
||||
t.Setenv("HOME", tmpDir)
|
||||
t.Setenv("XDG_CONFIG_HOME", tmpDir)
|
||||
|
||||
cfg, err := Load()
|
||||
@@ -186,6 +204,7 @@ func TestLoadSave(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg.Core.Shell = "zsh"
|
||||
cfg.Core.ShellLogin = true
|
||||
cfg.UI.MaxHeight = 20
|
||||
|
||||
err = Save(cfg)
|
||||
@@ -201,6 +220,9 @@ func TestLoadSave(t *testing.T) {
|
||||
if loaded.Core.Shell != "zsh" {
|
||||
t.Errorf("expected loaded shell to be zsh, got %q", loaded.Core.Shell)
|
||||
}
|
||||
if !loaded.Core.ShellLogin {
|
||||
t.Errorf("expected loaded login shell setting to be true")
|
||||
}
|
||||
if loaded.UI.MaxHeight != 20 {
|
||||
t.Errorf("expected loaded height to be 20, got %d", loaded.UI.MaxHeight)
|
||||
}
|
||||
@@ -250,5 +272,3 @@ func TestMigration(t *testing.T) {
|
||||
t.Errorf("expected backup file update_state.json.bak to exist")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ func DefaultConfig() *Config {
|
||||
Core: CoreConfig{
|
||||
Version: 1,
|
||||
Shell: "",
|
||||
ShellLogin: false,
|
||||
Mode: "last",
|
||||
Debug: false,
|
||||
ExpandAlias: true,
|
||||
|
||||
@@ -43,6 +43,9 @@ version = 1
|
||||
# override shell: "bash", "zsh", "fish", keep empty for auto detection
|
||||
shell = ""
|
||||
|
||||
# run the selected shell as a login shell
|
||||
shell-login = false
|
||||
|
||||
# startup mode: "last", "spec", "history"
|
||||
# "last" = remember last mode used
|
||||
mode = "last"
|
||||
|
||||
+18
-3
@@ -3,8 +3,10 @@ package root
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/versenilvis/iris/internal/config"
|
||||
)
|
||||
|
||||
func TestConfigCommands(t *testing.T) {
|
||||
@@ -14,14 +16,27 @@ func TestConfigCommands(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// UserConfigDir follows HOME on macOS and XDG_CONFIG_HOME on Unix.
|
||||
// Override both so the command cannot read or write the user's real config.
|
||||
t.Setenv("HOME", tmpDir)
|
||||
t.Setenv("XDG_CONFIG_HOME", tmpDir)
|
||||
|
||||
ConfigInitCmd.Run(ConfigInitCmd, []string{})
|
||||
|
||||
configPath := filepath.Join(tmpDir, "iris", "config.toml")
|
||||
if _, err := os.Stat(configPath); err != nil {
|
||||
configPath, err := config.ConfigPath()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get config path: %v", err)
|
||||
}
|
||||
if _, statErr := os.Stat(configPath); statErr != nil {
|
||||
t.Errorf("expected config file to be created at %s, but it was not", configPath)
|
||||
}
|
||||
content, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read config file: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(content), "shell-login = false") {
|
||||
t.Error("expected initialized config to include shell-login = false")
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
ConfigShowCmd.SetOut(buf)
|
||||
|
||||
+4
-1
@@ -148,7 +148,7 @@ var setupCmd = &cobra.Command{
|
||||
|
||||
var shellName string
|
||||
if len(args) > 0 {
|
||||
shellName = args[0]
|
||||
shellName = filepath.Base(args[0])
|
||||
} else {
|
||||
shellPath := os.Getenv("SHELL")
|
||||
shellName = filepath.Base(shellPath)
|
||||
@@ -208,6 +208,9 @@ version = 1
|
||||
# override shell: "bash", "zsh", "fish", keep empty for auto detection
|
||||
shell = ""
|
||||
|
||||
# run the selected shell as a login shell
|
||||
shell-login = false
|
||||
|
||||
# startup mode: "last", "spec", "history"
|
||||
# "last" = remember last mode used
|
||||
mode = "last"
|
||||
|
||||
+6
-1
@@ -51,23 +51,28 @@ It works exactly like coding editor suggestion menu drop down.`,
|
||||
},
|
||||
}
|
||||
shellFlag string
|
||||
shellLoginFlag bool
|
||||
debugMode bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&shellFlag, "shell", "s", "", "shell to use (bash, zsh, fish)")
|
||||
rootCmd.PersistentFlags().BoolVar(&shellLoginFlag, "shell-login", false, "run the selected shell as a login shell")
|
||||
rootCmd.PersistentFlags().BoolVarP(&debugMode, "debug", "d", false, "enable debug logging to iris.log")
|
||||
|
||||
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
if shellFlag != "" {
|
||||
config.Get().Core.Shell = shellFlag
|
||||
}
|
||||
if cmd.Flags().Changed("shell-login") {
|
||||
config.Get().Core.ShellLogin = shellLoginFlag
|
||||
}
|
||||
logDir, err := config.CachePath()
|
||||
if err == nil {
|
||||
logger.Init(filepath.Join(logDir, "iris.log"), debugMode || config.Get().Core.Debug)
|
||||
logger.Infof("IRIS session started: os=%s, arch=%s, go=%s, pid=%d", runtime.GOOS, runtime.GOARCH, runtime.Version(), os.Getpid())
|
||||
cfg := config.Get()
|
||||
logger.Debugf("IRIS loaded config: shell=%q, mode=%q, ghost-text=%v, max-suggestions=%d", cfg.Core.Shell, cfg.Core.Mode, cfg.UI.GhostText, cfg.UI.MaxSuggestions)
|
||||
logger.Debugf("IRIS loaded config: shell=%q, shell-login=%v, mode=%q, ghost-text=%v, max-suggestions=%d", cfg.Core.Shell, cfg.Core.ShellLogin, cfg.Core.Mode, cfg.UI.GhostText, cfg.UI.MaxSuggestions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShellArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
login bool
|
||||
want []string
|
||||
}{
|
||||
{name: "login", login: true, want: []string{"--login"}},
|
||||
{name: "non-login", login: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := shellArgs(tt.login); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Fatalf("shellArgs(%v) = %v, want %v", tt.login, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellLoginFlagIsRegistered(t *testing.T) {
|
||||
flag := rootCmd.PersistentFlags().Lookup("shell-login")
|
||||
if flag == nil {
|
||||
t.Fatal("expected --shell-login flag to be registered")
|
||||
}
|
||||
if flag.DefValue != "false" {
|
||||
t.Fatalf("expected --shell-login to default to false, got %q", flag.DefValue)
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -82,6 +82,13 @@ func saveMode(mode string) {
|
||||
_ = config.SaveState(state)
|
||||
}
|
||||
|
||||
func shellArgs(login bool) []string {
|
||||
if login {
|
||||
return []string{"--login"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
oldState *term.State
|
||||
oldStateFd int
|
||||
@@ -141,7 +148,7 @@ func runWrapper() {
|
||||
adapter := shell.Current
|
||||
|
||||
ctx := context.Background()
|
||||
c := exec.CommandContext(ctx, adapter.GetShellPath())
|
||||
c := exec.CommandContext(ctx, adapter.GetShellPath(), shellArgs(config.Get().Core.ShellLogin)...)
|
||||
c.ExtraFiles = make([]*os.File, 11)
|
||||
// pass write end of pipe to shell as fd 13 (since index 10 maps to 13)
|
||||
c.ExtraFiles[10] = w
|
||||
|
||||
Reference in New Issue
Block a user