Skip to content

Commit 19e64f5

Browse files
committed
options can now be set via env variables too
1 parent 4c195d8 commit 19e64f5

File tree

7 files changed

+470
-59
lines changed

7 files changed

+470
-59
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ https://github.com/user-attachments/assets/a6d16332-55a1-4866-9f3e-31490a488935
2121
* [Mistral](https://mistral.ai/)
2222
* [Anthropic](https://www.anthropic.com/)
2323
* Scriptable
24-
* All settings can be set via command line arguments
24+
* All settings can be set via:
25+
* environment variables
26+
* command line arguments
2527
* The users questions and models answers will be printed out in the terminal
2628
* Customizable
2729
* Choose between two themes (light and dark)

config/config_test.go

Lines changed: 255 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func TestConfig_Parse(t *testing.T) {
1717
tests := []struct {
1818
name string
1919
args []string
20+
env []string
2021
expected Config
2122
}{
2223
{
@@ -87,7 +88,6 @@ func TestConfig_Parse(t *testing.T) {
8788
c.UI.Prompt.SubmitShortcut = Shortcut{Code: "space"}
8889
}),
8990
},
90-
9191
{
9292
name: "Set UI file dialog default directory",
9393
args: []string{"--ui-file-dialog-default-dir", "/home/user"},
@@ -327,11 +327,264 @@ func TestConfig_Parse(t *testing.T) {
327327
c.PrintVersion = true
328328
}),
329329
},
330+
{
331+
name: "Set environment variable for init prompt",
332+
env: []string{EnvironmentPrefix + "UI_PROMPT_VALUE=test"},
333+
expected: modifiedConfig(func(c *Config) {
334+
c.UI.Prompt.InitValue = "test"
335+
}),
336+
},
337+
{
338+
name: "Set environment variable for log level",
339+
env: []string{EnvironmentPrefix + "LOG_LEVEL=-4"},
340+
expected: modifiedConfig(func(c *Config) {
341+
c.LogLevel = int(slog.LevelDebug)
342+
}),
343+
},
344+
{
345+
name: "Set environment variable for UI stream",
346+
env: []string{EnvironmentPrefix + "UI_STREAM=true"},
347+
expected: modifiedConfig(func(c *Config) {
348+
c.UI.Stream = true
349+
}),
350+
},
351+
{
352+
name: "Set environment variable for UI window title",
353+
env: []string{EnvironmentPrefix + "UI_WINDOW_TITLE=Test Title"},
354+
expected: modifiedConfig(func(c *Config) {
355+
c.UI.Window.Title = "Test Title"
356+
}),
357+
},
358+
{
359+
name: "Set environment variable for backend",
360+
env: []string{EnvironmentPrefix + "BACKEND=openai"},
361+
expected: modifiedConfig(func(c *Config) {
362+
c.LLM.Backend = llm.BackendOpenAI
363+
}),
364+
},
365+
{
366+
name: "Set environment variable for print format",
367+
env: []string{EnvironmentPrefix + "PRINT_FORMAT=plain"},
368+
expected: modifiedConfig(func(c *Config) {
369+
c.Printer.Format = PrinterFormatPlain
370+
}),
371+
},
372+
{
373+
name: "Set environment variable for UI language",
374+
env: []string{EnvironmentPrefix + "UI_LANG=en_US"},
375+
expected: modifiedConfig(func(c *Config) {
376+
c.UI.Language = "en_US"
377+
}),
378+
},
379+
{
380+
name: "Set environment variable for UI prompt initial attachments",
381+
env: []string{
382+
EnvironmentPrefix + "UI_PROMPT_ATTACHMENTS_1=file2.txt",
383+
EnvironmentPrefix + "UI_PROMPT_ATTACHMENTS_0=file1.txt",
384+
},
385+
expected: modifiedConfig(func(c *Config) {
386+
c.UI.Prompt.InitAttachments = []string{"file1.txt", "file2.txt"}
387+
}),
388+
},
389+
{
390+
name: "Set environment variable for UI prompt min rows",
391+
env: []string{EnvironmentPrefix + "UI_PROMPT_MIN_ROWS=2"},
392+
expected: modifiedConfig(func(c *Config) {
393+
c.UI.Prompt.MinRows = 2
394+
}),
395+
},
396+
{
397+
name: "Set environment variable for UI prompt max rows",
398+
env: []string{EnvironmentPrefix + "UI_PROMPT_MAX_ROWS=5"},
399+
expected: modifiedConfig(func(c *Config) {
400+
c.UI.Prompt.MaxRows = 5
401+
}),
402+
},
403+
{
404+
name: "Set environment variable for UI prompt submit key",
405+
env: []string{
406+
EnvironmentPrefix + "UI_PROMPT_SUBMIT_ALT=false",
407+
EnvironmentPrefix + "UI_PROMPT_SUBMIT_KEY=space",
408+
},
409+
expected: modifiedConfig(func(c *Config) {
410+
c.UI.Prompt.SubmitShortcut = Shortcut{Code: "space"}
411+
}),
412+
},
413+
{
414+
name: "Set environment variable for UI file dialog default directory",
415+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_DEFAULT_DIR=/home/user"},
416+
expected: modifiedConfig(func(c *Config) {
417+
c.UI.FileDialog.DefaultDirectory = "/home/user"
418+
}),
419+
},
420+
{
421+
name: "Set environment variable for UI file dialog show hidden files",
422+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_SHOW_HIDDEN=false"},
423+
expected: modifiedConfig(func(c *Config) {
424+
c.UI.FileDialog.ShowHiddenFiles = false
425+
}),
426+
},
427+
{
428+
name: "Set environment variable for UI file dialog can create directories",
429+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_CAN_CREATE_DIRS=true"},
430+
expected: modifiedConfig(func(c *Config) {
431+
c.UI.FileDialog.CanCreateDirectories = true
432+
}),
433+
},
434+
{
435+
name: "Set environment variable for UI file dialog resolves aliases",
436+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_RESOLVES_ALIASES=true"},
437+
expected: modifiedConfig(func(c *Config) {
438+
c.UI.FileDialog.ResolvesAliases = true
439+
}),
440+
},
441+
{
442+
name: "Set environment variable for UI file dialog treat packages as directories",
443+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_TREAT_PACKAGES_AS_DIRS=false"},
444+
expected: modifiedConfig(func(c *Config) {
445+
c.UI.FileDialog.TreatPackagesAsDirectories = false
446+
}),
447+
},
448+
{
449+
name: "Set environment variable for UI file dialog filter display",
450+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_FILTER_DISPLAY_0=Images (*.jpg, *.png)"},
451+
expected: modifiedConfig(func(c *Config) {
452+
c.UI.FileDialog.FilterDisplay = []string{"Images (*.jpg, *.png)"}
453+
}),
454+
},
455+
{
456+
name: "Set environment variable for UI file dialog filter pattern",
457+
env: []string{EnvironmentPrefix + "UI_FILE_DIALOG_FILTER_PATTERN_0=*.jpg;*.png"},
458+
expected: modifiedConfig(func(c *Config) {
459+
c.UI.FileDialog.FilterPattern = []string{"*.jpg;*.png"}
460+
}),
461+
},
462+
{
463+
name: "Set environment variable for UI initial width",
464+
env: []string{EnvironmentPrefix + "UI_WINDOW_INIT_WIDTH=100"},
465+
expected: modifiedConfig(func(c *Config) {
466+
c.UI.Window.InitialWidth = ExpressionContainer{Expression: "100"}
467+
}),
468+
},
469+
{
470+
name: "Set environment variable for UI max height",
471+
env: []string{EnvironmentPrefix + "UI_WINDOW_MAX_HEIGHT=200"},
472+
expected: modifiedConfig(func(c *Config) {
473+
c.UI.Window.MaxHeight = ExpressionContainer{Expression: "200"}
474+
}),
475+
},
476+
{
477+
name: "Set environment variable for UI initial position X",
478+
env: []string{EnvironmentPrefix + "UI_WINDOW_INIT_POS_X=50"},
479+
expected: modifiedConfig(func(c *Config) {
480+
c.UI.Window.InitialPositionX = ExpressionContainer{Expression: "50"}
481+
}),
482+
},
483+
{
484+
name: "Set environment variable for UI initial position Y",
485+
env: []string{EnvironmentPrefix + "UI_WINDOW_INIT_POS_Y=50"},
486+
expected: modifiedConfig(func(c *Config) {
487+
c.UI.Window.InitialPositionY = ExpressionContainer{Expression: "50"}
488+
}),
489+
},
490+
{
491+
name: "Set environment variable for UI initial zoom",
492+
env: []string{EnvironmentPrefix + "UI_WINDOW_INIT_ZOOM=1.5"},
493+
expected: modifiedConfig(func(c *Config) {
494+
c.UI.Window.InitialZoom = ExpressionContainer{Expression: "1.5"}
495+
}),
496+
},
497+
{
498+
name: "Set environment variable for UI background color",
499+
env: []string{
500+
EnvironmentPrefix + "UI_WINDOW_BG_COLOR_R=100",
501+
EnvironmentPrefix + "UI_WINDOW_BG_COLOR_G=100",
502+
EnvironmentPrefix + "UI_WINDOW_BG_COLOR_B=100",
503+
EnvironmentPrefix + "UI_WINDOW_BG_COLOR_A=100",
504+
},
505+
expected: modifiedConfig(func(c *Config) {
506+
c.UI.Window.BackgroundColor = WindowBackgroundColor{R: 100, G: 100, B: 100, A: 100}
507+
}),
508+
},
509+
{
510+
name: "Set environment variable for UI start state",
511+
env: []string{EnvironmentPrefix + "UI_WINDOW_START_STATE=1"},
512+
expected: modifiedConfig(func(c *Config) {
513+
c.UI.Window.StartState = 1
514+
}),
515+
},
516+
{
517+
name: "Set environment variable for UI frameless",
518+
env: []string{EnvironmentPrefix + "UI_WINDOW_FRAMELESS=false"},
519+
expected: modifiedConfig(func(c *Config) {
520+
c.UI.Window.Frameless = false
521+
}),
522+
},
523+
{
524+
name: "Set environment variable for UI always on top",
525+
env: []string{EnvironmentPrefix + "UI_WINDOW_ALWAYS_ON_TOP=false"},
526+
expected: modifiedConfig(func(c *Config) {
527+
c.UI.Window.AlwaysOnTop = false
528+
}),
529+
},
530+
{
531+
name: "Set environment variable for UI resizable",
532+
env: []string{EnvironmentPrefix + "UI_WINDOW_RESIZEABLE=false"},
533+
expected: modifiedConfig(func(c *Config) {
534+
c.UI.Window.Resizeable = false
535+
}),
536+
},
537+
{
538+
name: "Set environment variable for UI translucent",
539+
env: []string{EnvironmentPrefix + "UI_WINDOW_TRANSLUCENT=never"},
540+
expected: modifiedConfig(func(c *Config) {
541+
c.UI.Window.Translucent = TranslucentNever
542+
}),
543+
},
544+
{
545+
name: "Set environment variable for UI quit shortcut",
546+
env: []string{
547+
EnvironmentPrefix + "UI_QUIT_SHORTCUT_KEY=q",
548+
EnvironmentPrefix + "UI_QUIT_SHORTCUT_CTRL=true",
549+
},
550+
expected: modifiedConfig(func(c *Config) {
551+
c.UI.QuitShortcut = Shortcut{Code: "q", Ctrl: true}
552+
}),
553+
},
554+
{
555+
name: "Set environment variable for UI theme",
556+
env: []string{EnvironmentPrefix + "UI_THEME=dark"},
557+
expected: modifiedConfig(func(c *Config) {
558+
c.UI.Theme = ThemeDark
559+
}),
560+
},
561+
{
562+
name: "Set environment variable for UI code style",
563+
env: []string{EnvironmentPrefix + "UI_CODE_STYLE=monokai"},
564+
expected: modifiedConfig(func(c *Config) {
565+
c.UI.CodeStyle = "monokai"
566+
}),
567+
},
568+
{
569+
name: "Set environment variable for print version",
570+
env: []string{EnvironmentPrefix + "VERSION=true"},
571+
expected: modifiedConfig(func(c *Config) {
572+
c.PrintVersion = true
573+
}),
574+
},
575+
{
576+
name: "Argument will override environment",
577+
args: []string{"--ui-prompt-value=arg-prompt"},
578+
env: []string{EnvironmentPrefix + "UI_PROMPT_VALUE=env-prompt"},
579+
expected: modifiedConfig(func(c *Config) {
580+
c.UI.Prompt.InitValue = "arg-prompt"
581+
}),
582+
},
330583
}
331584

332585
for _, tt := range tests {
333586
t.Run(tt.name, func(t *testing.T) {
334-
c := Parse(tt.args)
587+
c := Parse(tt.args, tt.env)
335588
assert.Equal(t, tt.expected, *c)
336589
})
337590
}

config/help.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"io"
1313
"net/http"
1414
"os"
15+
"sort"
16+
"strings"
1517
)
1618

1719
type NoopLooger struct {
@@ -38,10 +40,29 @@ func (n NoopLooger) Error(message string) {
3840
func (n NoopLooger) Fatal(message string) {
3941
}
4042

41-
func printUsage(output io.Writer) {
43+
func printUsage(output io.Writer, fields resolvedFieldInfos) {
4244
fmt.Fprintf(output, "Usage of %s:\n", os.Args[0])
4345
flag.PrintDefaults()
4446

47+
fmt.Fprintf(output, "\nAvailable environment variables:\n")
48+
49+
maxLen := 0
50+
sort.Slice(fields, func(i, j int) bool {
51+
//the sort algorithm must iterate all elements
52+
//so here we "recycle" the loop to determine max-length
53+
if len(fields[i].Env) > maxLen {
54+
maxLen = len(fields[i].Env)
55+
}
56+
return fields[i].Env < fields[j].Env
57+
})
58+
for _, field := range fields {
59+
env := field.Env
60+
if strings.HasPrefix(field.Value.Type().String(), "*[]") {
61+
env += "_N"
62+
}
63+
fmt.Fprintf(output, " %s%s\t%s\n", env, strings.Repeat(" ", maxLen-len(env)), field.Usage)
64+
}
65+
4566
fmt.Fprintf(output, "\nAvailable code styles:\n")
4667
for _, style := range availableCodeStyles {
4768
fmt.Fprintf(output, " - %s\n", style)

0 commit comments

Comments
 (0)