Skip to content

Commit 76ae208

Browse files
committed
Support default values in ${env}
1 parent e182d57 commit 76ae208

File tree

4 files changed

+43
-8
lines changed

4 files changed

+43
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Support `${classapath:path/to/eca/classpath/file}` in dynamic string parse.
6+
- Support default env values in `${env:MY_ENV:default-value}`.
67
- Deprecate configs:
78
- `systemPromptFile` in favor of `systemPrompt` using `${file:...}` or `${classpath:...}`
89

docs/configuration.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,7 @@ To configure, add your OTLP collector config via `:otlp` map following [otlp aut
475475
providers?: {[key: string]: {
476476
api?: 'openai-responses' | 'openai-chat' | 'anthropic';
477477
url?: string;
478-
urlEnv?: string;
479478
key?: string; // when provider supports api key.
480-
keyEnv?: string;
481479
keyRc?: string; // credential file lookup in format [login@]machine[:port]
482480
completionUrlRelativePath?: string;
483481
thinkTagStart?: string;

src/eca/config.clj

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@
8484
:requiresAuth? true
8585
:models {"gemini-2.0-flash" {}
8686
"gemini-2.5-pro" {}}}
87-
"ollama" {:url "http://localhost:11434"
88-
:urlEnv "OLLAMA_API_URL"}}
87+
"ollama" {:url "${env:OLLAMA_API_URL:http://localhost:11434}"}}
8988
:defaultBehavior "agent"
9089
:behavior {"agent" {:systemPrompt "${classpath:prompts/agent_behavior.md}"
9190
:disabledTools ["preview_file_change"]}
@@ -149,14 +148,14 @@
149148

150149
(defn ^:private parse-dynamic-string
151150
"Given a string and a current working directory, look for patterns replacing its content:
152-
- `${env:SOME-ENV}`: Replace with a env
151+
- `${env:SOME-ENV:default-value}`: Replace with a env falling back to a optional default value
153152
- `${file:/some/path}`: Replace with a file content checking from cwd if relative
154153
- `${classpath:path/to/file}`: Replace with a file content found checking classpath"
155154
[s cwd]
156155
(some-> s
157-
(string/replace #"\$\{env:([^}]+)\}"
158-
(fn [[_match env-var]]
159-
(or (get-env env-var) "")))
156+
(string/replace #"\$\{env:([^:}]+)(?::([^}]*))?\}"
157+
(fn [[_match env-var default-value]]
158+
(or (get-env env-var) default-value "")))
160159
(string/replace #"\$\{file:([^}]+)\}"
161160
(fn [[_match file-path]]
162161
(try

test/eca/config_test.clj

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,43 @@
209209
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR}" "/tmp")))
210210
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR} suffix" "/tmp")))))
211211

212+
(testing "replaces undefined env var with default value"
213+
(with-redefs [config/get-env (constantly nil)]
214+
(is (= "default-value" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:default-value}" "/tmp")))
215+
(is (= "http://localhost:11434" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp")))
216+
(is (= "prefix default-value suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:default-value} suffix" "/tmp")))))
217+
218+
(testing "uses env var value when set, ignoring default"
219+
(with-redefs [config/get-env (fn [env-var]
220+
(case env-var
221+
"TEST_VAR" "actual-value"
222+
"OLLAMA_API_URL" "http://custom:8080"
223+
nil))]
224+
(is (= "actual-value" (#'config/parse-dynamic-string "${env:TEST_VAR:default-value}" "/tmp")))
225+
(is (= "http://custom:8080" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp")))))
226+
227+
(testing "handles default values with special characters"
228+
(with-redefs [config/get-env (constantly nil)]
229+
(is (= "http://localhost:11434/api" (#'config/parse-dynamic-string "${env:API_URL:http://localhost:11434/api}" "/tmp")))
230+
(is (= "value-with-dashes" (#'config/parse-dynamic-string "${env:VAR:value-with-dashes}" "/tmp")))
231+
(is (= "value_with_underscores" (#'config/parse-dynamic-string "${env:VAR:value_with_underscores}" "/tmp")))
232+
(is (= "/path/to/file" (#'config/parse-dynamic-string "${env:VAR:/path/to/file}" "/tmp")))))
233+
234+
(testing "handles empty default value"
235+
(with-redefs [config/get-env (constantly nil)]
236+
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:}" "/tmp")))
237+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:} suffix" "/tmp")))))
238+
239+
(testing "handles multiple env vars with mixed default values"
240+
(with-redefs [config/get-env (fn [env-var]
241+
(case env-var
242+
"DEFINED_VAR" "defined"
243+
nil))]
244+
(is (= "defined and default-value"
245+
(#'config/parse-dynamic-string "${env:DEFINED_VAR:fallback1} and ${env:UNDEFINED_VAR:default-value}" "/tmp")))
246+
(is (= "defined and "
247+
(#'config/parse-dynamic-string "${env:DEFINED_VAR} and ${env:UNDEFINED_VAR}" "/tmp")))))
248+
212249
(testing "replaces file pattern with file content - absolute path"
213250
(with-redefs [fs/absolute? (fn [path] (= path "/absolute/file.txt"))
214251
slurp (fn [path]

0 commit comments

Comments
 (0)