Skip to content

Commit 8ca2ddb

Browse files
committed
Fix ${netrc:...} to consider :netrcFile config.
1 parent 01247bf commit 8ca2ddb

File tree

3 files changed

+65
-63
lines changed

3 files changed

+65
-63
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Fix `${netrc:...} ` to consider `:netrcFile` config.
6+
57
## 0.84.0
68

79
- Improve `/compact` UI in chat after running, cleaning chat and showing the new summary.

src/eca/config.clj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
- `${file:/some/path}`: Replace with a file content checking from cwd if relative
150150
- `${classpath:path/to/file}`: Replace with a file content found checking classpath
151151
- `${netrc:api.provider.com}`: Replace with the content from Unix net RC [credential files](https://eca.dev/models/#credential-file-authentication)"
152-
[s cwd]
152+
[s cwd config]
153153
(some-> s
154154
(string/replace #"\$\{env:([^:}]+)(?::([^}]*))?\}"
155155
(fn [[_match env-var default-value]]
@@ -175,7 +175,7 @@
175175
(string/replace #"\$\{netrc:([^}]+)\}"
176176
(fn [[_match key-rc]]
177177
(try
178-
(or (secrets/get-credential key-rc) "")
178+
(or (secrets/get-credential key-rc (:netrcFile config)) "")
179179
(catch Exception e
180180
(logger/warn logger-tag "Error reading netrc credential:" (.getMessage e))
181181
""))))))
@@ -184,11 +184,11 @@
184184
"walk through config parsing dynamic string contents if value is a string."
185185
[config cwd]
186186
(walk/postwalk
187-
(fn [x]
188-
(if (string? x)
189-
(parse-dynamic-string x cwd)
190-
x))
191-
config))
187+
(fn [x]
188+
(if (string? x)
189+
(parse-dynamic-string x cwd config)
190+
x))
191+
config))
192192

193193
(def initial-config (memoize #(parse-dynamic-string-values initial-config* nil)))
194194

test/eca/config_test.clj

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -190,70 +190,70 @@
190190

191191
(deftest parse-dynamic-string-test
192192
(testing "returns nil for nil input"
193-
(is (nil? (#'config/parse-dynamic-string nil "/tmp"))))
193+
(is (nil? (#'config/parse-dynamic-string nil "/tmp" {}))))
194194

195195
(testing "returns string unchanged when no patterns"
196-
(is (= "hello world" (#'config/parse-dynamic-string "hello world" "/tmp"))))
196+
(is (= "hello world" (#'config/parse-dynamic-string "hello world" "/tmp" {}))))
197197

198198
(testing "replaces environment variable patterns"
199199
(with-redefs [config/get-env (fn [env-var]
200200
(case env-var
201201
"TEST_VAR" "test-value"
202202
"ANOTHER_VAR" "another-value"
203203
nil))]
204-
(is (= "test-value" (#'config/parse-dynamic-string "${env:TEST_VAR}" "/tmp")))
205-
(is (= "prefix test-value suffix" (#'config/parse-dynamic-string "prefix ${env:TEST_VAR} suffix" "/tmp")))
206-
(is (= "test-value and another-value" (#'config/parse-dynamic-string "${env:TEST_VAR} and ${env:ANOTHER_VAR}" "/tmp")))))
204+
(is (= "test-value" (#'config/parse-dynamic-string "${env:TEST_VAR}" "/tmp" {})))
205+
(is (= "prefix test-value suffix" (#'config/parse-dynamic-string "prefix ${env:TEST_VAR} suffix" "/tmp" {})))
206+
(is (= "test-value and another-value" (#'config/parse-dynamic-string "${env:TEST_VAR} and ${env:ANOTHER_VAR}" "/tmp" {})))))
207207

208208
(testing "replaces undefined env var with empty string"
209209
(with-redefs [config/get-env (constantly nil)]
210-
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR}" "/tmp")))
211-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR} suffix" "/tmp")))))
210+
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR}" "/tmp" {})))
211+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR} suffix" "/tmp" {})))))
212212

213213
(testing "replaces undefined env var with default value"
214214
(with-redefs [config/get-env (constantly nil)]
215-
(is (= "default-value" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:default-value}" "/tmp")))
216-
(is (= "http://localhost:11434" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp")))
217-
(is (= "prefix default-value suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:default-value} suffix" "/tmp")))))
215+
(is (= "default-value" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:default-value}" "/tmp" {})))
216+
(is (= "http://localhost:11434" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp" {})))
217+
(is (= "prefix default-value suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:default-value} suffix" "/tmp" {})))))
218218

219219
(testing "uses env var value when set, ignoring default"
220220
(with-redefs [config/get-env (fn [env-var]
221221
(case env-var
222222
"TEST_VAR" "actual-value"
223223
"OLLAMA_API_URL" "http://custom:8080"
224224
nil))]
225-
(is (= "actual-value" (#'config/parse-dynamic-string "${env:TEST_VAR:default-value}" "/tmp")))
226-
(is (= "http://custom:8080" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp")))))
225+
(is (= "actual-value" (#'config/parse-dynamic-string "${env:TEST_VAR:default-value}" "/tmp" {})))
226+
(is (= "http://custom:8080" (#'config/parse-dynamic-string "${env:OLLAMA_API_URL:http://localhost:11434}" "/tmp" {})))))
227227

228228
(testing "handles default values with special characters"
229229
(with-redefs [config/get-env (constantly nil)]
230-
(is (= "http://localhost:11434/api" (#'config/parse-dynamic-string "${env:API_URL:http://localhost:11434/api}" "/tmp")))
231-
(is (= "value-with-dashes" (#'config/parse-dynamic-string "${env:VAR:value-with-dashes}" "/tmp")))
232-
(is (= "value_with_underscores" (#'config/parse-dynamic-string "${env:VAR:value_with_underscores}" "/tmp")))
233-
(is (= "/path/to/file" (#'config/parse-dynamic-string "${env:VAR:/path/to/file}" "/tmp")))))
230+
(is (= "http://localhost:11434/api" (#'config/parse-dynamic-string "${env:API_URL:http://localhost:11434/api}" "/tmp" {})))
231+
(is (= "value-with-dashes" (#'config/parse-dynamic-string "${env:VAR:value-with-dashes}" "/tmp" {})))
232+
(is (= "value_with_underscores" (#'config/parse-dynamic-string "${env:VAR:value_with_underscores}" "/tmp" {})))
233+
(is (= "/path/to/file" (#'config/parse-dynamic-string "${env:VAR:/path/to/file}" "/tmp" {})))))
234234

235235
(testing "handles empty default value"
236236
(with-redefs [config/get-env (constantly nil)]
237-
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:}" "/tmp")))
238-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:} suffix" "/tmp")))))
237+
(is (= "" (#'config/parse-dynamic-string "${env:UNDEFINED_VAR:}" "/tmp" {})))
238+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${env:UNDEFINED_VAR:} suffix" "/tmp" {})))))
239239

240240
(testing "handles multiple env vars with mixed default values"
241241
(with-redefs [config/get-env (fn [env-var]
242242
(case env-var
243243
"DEFINED_VAR" "defined"
244244
nil))]
245245
(is (= "defined and default-value"
246-
(#'config/parse-dynamic-string "${env:DEFINED_VAR:fallback1} and ${env:UNDEFINED_VAR:default-value}" "/tmp")))
246+
(#'config/parse-dynamic-string "${env:DEFINED_VAR:fallback1} and ${env:UNDEFINED_VAR:default-value}" "/tmp" {})))
247247
(is (= "defined and "
248-
(#'config/parse-dynamic-string "${env:DEFINED_VAR} and ${env:UNDEFINED_VAR}" "/tmp")))))
248+
(#'config/parse-dynamic-string "${env:DEFINED_VAR} and ${env:UNDEFINED_VAR}" "/tmp" {})))))
249249

250250
(testing "replaces file pattern with file content - absolute path"
251251
(with-redefs [fs/absolute? (fn [path] (= path "/absolute/file.txt"))
252252
slurp (fn [path]
253253
(if (= (str path) "/absolute/file.txt")
254254
"test file content"
255255
(throw (ex-info "File not found" {}))))]
256-
(is (= "test file content" (#'config/parse-dynamic-string "${file:/absolute/file.txt}" "/tmp")))))
256+
(is (= "test file content" (#'config/parse-dynamic-string "${file:/absolute/file.txt}" "/tmp" {})))))
257257

258258
(testing "replaces file pattern with file content - relative path"
259259
(with-redefs [fs/absolute? (fn [_] false)
@@ -262,14 +262,14 @@
262262
(if (= path "/tmp/test.txt")
263263
"relative file content"
264264
(throw (ex-info "File not found" {}))))]
265-
(is (= "relative file content" (#'config/parse-dynamic-string "${file:test.txt}" "/tmp")))))
265+
(is (= "relative file content" (#'config/parse-dynamic-string "${file:test.txt}" "/tmp" {})))))
266266

267267
(testing "replaces file pattern with empty string when file not found"
268268
(with-redefs [logger/warn (fn [& _] nil)
269269
fs/absolute? (fn [_] true)
270270
slurp (fn [_] (throw (ex-info "File not found" {})))]
271-
(is (= "" (#'config/parse-dynamic-string "${file:/nonexistent/file.txt}" "/tmp")))
272-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${file:/nonexistent/file.txt} suffix" "/tmp")))))
271+
(is (= "" (#'config/parse-dynamic-string "${file:/nonexistent/file.txt}" "/tmp" {})))
272+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${file:/nonexistent/file.txt} suffix" "/tmp" {})))))
273273

274274
(testing "handles multiple file patterns"
275275
(with-redefs [fs/absolute? (fn [_] true)
@@ -279,7 +279,7 @@
279279
"/file2.txt" "content2"
280280
(throw (ex-info "File not found" {}))))]
281281
(is (= "content1 and content2"
282-
(#'config/parse-dynamic-string "${file:/file1.txt} and ${file:/file2.txt}" "/tmp")))))
282+
(#'config/parse-dynamic-string "${file:/file1.txt} and ${file:/file2.txt}" "/tmp" {})))))
283283

284284
(testing "handles mixed env and file patterns"
285285
(with-redefs [config/get-env (fn [env-var]
@@ -290,30 +290,30 @@
290290
"file-value"
291291
(throw (ex-info "File not found" {}))))]
292292
(is (= "env-value and file-value"
293-
(#'config/parse-dynamic-string "${env:TEST_VAR} and ${file:/file.txt}" "/tmp")))))
293+
(#'config/parse-dynamic-string "${env:TEST_VAR} and ${file:/file.txt}" "/tmp" {})))))
294294

295295
(testing "handles patterns within longer strings"
296296
(with-redefs [config/get-env (fn [env-var]
297297
(when (= env-var "API_KEY") "secret123"))]
298-
(is (= "Bearer secret123" (#'config/parse-dynamic-string "Bearer ${env:API_KEY}" "/tmp")))))
298+
(is (= "Bearer secret123" (#'config/parse-dynamic-string "Bearer ${env:API_KEY}" "/tmp" {})))))
299299

300300
(testing "handles empty string input"
301-
(is (= "" (#'config/parse-dynamic-string "" "/tmp"))))
301+
(is (= "" (#'config/parse-dynamic-string "" "/tmp" {}))))
302302

303303
(testing "preserves content with escaped-like patterns that don't match"
304-
(is (= "${notenv:VAR}" (#'config/parse-dynamic-string "${notenv:VAR}" "/tmp")))
305-
(is (= "${env:}" (#'config/parse-dynamic-string "${env:}" "/tmp"))))
304+
(is (= "${notenv:VAR}" (#'config/parse-dynamic-string "${notenv:VAR}" "/tmp" {})))
305+
(is (= "${env:}" (#'config/parse-dynamic-string "${env:}" "/tmp" {}))))
306306

307307
(testing "replaces classpath pattern with resource content"
308308
;; ECA_VERSION is a real resource file
309-
(let [version-content (#'config/parse-dynamic-string "${classpath:ECA_VERSION}" "/tmp")]
309+
(let [version-content (#'config/parse-dynamic-string "${classpath:ECA_VERSION}" "/tmp" {})]
310310
(is (string? version-content))
311311
(is (seq version-content))))
312312

313313
(testing "replaces classpath pattern with empty string when resource not found"
314314
(with-redefs [logger/warn (fn [& _] nil)]
315-
(is (= "" (#'config/parse-dynamic-string "${classpath:nonexistent/resource.txt}" "/tmp")))
316-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${classpath:nonexistent/resource.txt} suffix" "/tmp")))))
315+
(is (= "" (#'config/parse-dynamic-string "${classpath:nonexistent/resource.txt}" "/tmp" {})))
316+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${classpath:nonexistent/resource.txt} suffix" "/tmp" {})))))
317317

318318
(testing "handles multiple classpath patterns"
319319
(with-redefs [io/resource (fn [path]
@@ -322,51 +322,51 @@
322322
"resource2.txt" (java.io.ByteArrayInputStream. (.getBytes "content2" "UTF-8"))
323323
nil))]
324324
(is (= "content1 and content2"
325-
(#'config/parse-dynamic-string "${classpath:resource1.txt} and ${classpath:resource2.txt}" "/tmp")))))
325+
(#'config/parse-dynamic-string "${classpath:resource1.txt} and ${classpath:resource2.txt}" "/tmp" {})))))
326326

327327
(testing "handles classpath patterns within longer strings"
328328
(with-redefs [io/resource (fn [path]
329329
(when (= path "config/prompt.md")
330330
(java.io.ByteArrayInputStream. (.getBytes "# System Prompt\nYou are helpful." "UTF-8"))))]
331331
(is (= "# System Prompt\nYou are helpful."
332-
(#'config/parse-dynamic-string "${classpath:config/prompt.md}" "/tmp")))))
332+
(#'config/parse-dynamic-string "${classpath:config/prompt.md}" "/tmp" {})))))
333333

334334
(testing "handles exception when reading classpath resource throws NullPointerException"
335335
(with-redefs [logger/warn (fn [& _] nil)
336336
io/resource (constantly nil)]
337-
(is (= "" (#'config/parse-dynamic-string "${classpath:error/resource.txt}" "/tmp")))
338-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${classpath:error/resource.txt} suffix" "/tmp")))))
337+
(is (= "" (#'config/parse-dynamic-string "${classpath:error/resource.txt}" "/tmp" {})))
338+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${classpath:error/resource.txt} suffix" "/tmp" {})))))
339339

340340
(testing "replaces netrc pattern with credential password"
341-
(with-redefs [secrets/get-credential (fn [key-rc]
341+
(with-redefs [secrets/get-credential (fn [key-rc _]
342342
(when (= key-rc "api.openai.com")
343343
"secret-password-123"))]
344-
(is (= "secret-password-123" (#'config/parse-dynamic-string "${netrc:api.openai.com}" "/tmp")))))
344+
(is (= "secret-password-123" (#'config/parse-dynamic-string "${netrc:api.openai.com}" "/tmp" {})))))
345345

346346
(testing "replaces netrc pattern with empty string when credential not found"
347347
(with-redefs [secrets/get-credential (constantly nil)]
348-
(is (= "" (#'config/parse-dynamic-string "${netrc:nonexistent.com}" "/tmp")))
349-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${netrc:nonexistent.com} suffix" "/tmp")))))
348+
(is (= "" (#'config/parse-dynamic-string "${netrc:nonexistent.com}" "/tmp" {})))
349+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${netrc:nonexistent.com} suffix" "/tmp" {})))))
350350

351351
(testing "handles netrc pattern with login and port"
352-
(with-redefs [secrets/get-credential (fn [key-rc]
352+
(with-redefs [secrets/get-credential (fn [key-rc _]
353353
(case key-rc
354354
"[email protected]" "password1"
355355
"api.example.com:8080" "password2"
356356
"[email protected]:443" "password3"
357357
nil))]
358-
(is (= "password1" (#'config/parse-dynamic-string "${netrc:[email protected]}" "/tmp")))
359-
(is (= "password2" (#'config/parse-dynamic-string "${netrc:api.example.com:8080}" "/tmp")))
360-
(is (= "password3" (#'config/parse-dynamic-string "${netrc:[email protected]:443}" "/tmp")))))
358+
(is (= "password1" (#'config/parse-dynamic-string "${netrc:[email protected]}" "/tmp" {})))
359+
(is (= "password2" (#'config/parse-dynamic-string "${netrc:api.example.com:8080}" "/tmp" {})))
360+
(is (= "password3" (#'config/parse-dynamic-string "${netrc:[email protected]:443}" "/tmp" {})))))
361361

362362
(testing "handles multiple netrc patterns"
363-
(with-redefs [secrets/get-credential (fn [key-rc]
363+
(with-redefs [secrets/get-credential (fn [key-rc _]
364364
(case key-rc
365365
"api1.example.com" "password1"
366366
"api2.example.com" "password2"
367367
nil))]
368368
(is (= "password1 and password2"
369-
(#'config/parse-dynamic-string "${netrc:api1.example.com} and ${netrc:api2.example.com}" "/tmp")))))
369+
(#'config/parse-dynamic-string "${netrc:api1.example.com} and ${netrc:api2.example.com}" "/tmp" {})))))
370370

371371
(testing "handles mixed env, file, classpath, and netrc patterns"
372372
(with-redefs [config/get-env (fn [env-var]
@@ -380,30 +380,30 @@
380380
(throw (ex-info "File not found" {})))
381381
:else "classpath-value"))
382382
io/resource (fn [_] (java.io.ByteArrayInputStream. (.getBytes "classpath-value" "UTF-8")))
383-
secrets/get-credential (fn [key-rc]
383+
secrets/get-credential (fn [key-rc _]
384384
(when (= key-rc "api.example.com")
385385
"netrc-password"))
386386
logger/warn (fn [& _] nil)]
387387
(is (= "env-value and file-value and classpath-value and netrc-password"
388-
(#'config/parse-dynamic-string "${env:TEST_VAR} and ${file:/file.txt} and ${classpath:resource.txt} and ${netrc:api.example.com}" "/tmp")))))
388+
(#'config/parse-dynamic-string "${env:TEST_VAR} and ${file:/file.txt} and ${classpath:resource.txt} and ${netrc:api.example.com}" "/tmp" {})))))
389389

390390
(testing "handles netrc pattern within longer strings"
391-
(with-redefs [secrets/get-credential (fn [key-rc]
391+
(with-redefs [secrets/get-credential (fn [key-rc _]
392392
(when (= key-rc "api.openai.com")
393393
"sk-abc123"))]
394-
(is (= "Bearer sk-abc123" (#'config/parse-dynamic-string "Bearer ${netrc:api.openai.com}" "/tmp")))))
394+
(is (= "Bearer sk-abc123" (#'config/parse-dynamic-string "Bearer ${netrc:api.openai.com}" "/tmp" {})))))
395395

396396
(testing "handles exception when reading netrc credential fails"
397397
(with-redefs [logger/warn (fn [& _] nil)
398398
secrets/get-credential (fn [_] (throw (ex-info "Netrc error" {})))]
399-
(is (= "" (#'config/parse-dynamic-string "${netrc:api.example.com}" "/tmp")))
400-
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${netrc:api.example.com} suffix" "/tmp")))))
399+
(is (= "" (#'config/parse-dynamic-string "${netrc:api.example.com}" "/tmp" {})))
400+
(is (= "prefix suffix" (#'config/parse-dynamic-string "prefix ${netrc:api.example.com} suffix" "/tmp" {})))))
401401

402402
(testing "handles netrc pattern with special characters in key-rc"
403-
(with-redefs [secrets/get-credential (fn [key-rc]
403+
(with-redefs [secrets/get-credential (fn [key-rc _]
404404
(case key-rc
405405
"api-gateway.example-corp.com" "password1"
406406
"api_service.example.com" "password2"
407407
nil))]
408-
(is (= "password1" (#'config/parse-dynamic-string "${netrc:api-gateway.example-corp.com}" "/tmp")))
409-
(is (= "password2" (#'config/parse-dynamic-string "${netrc:api_service.example.com}" "/tmp"))))))
408+
(is (= "password1" (#'config/parse-dynamic-string "${netrc:api-gateway.example-corp.com}" "/tmp" {})))
409+
(is (= "password2" (#'config/parse-dynamic-string "${netrc:api_service.example.com}" "/tmp" {}))))))

0 commit comments

Comments
 (0)