Neither the ELASTIC_* environment variables nor edits to conf/application.conf have any effect on the REPL's Elasticsearch connection. Only the command-line flags (-h, -p, …) work.
Reproduced on the published 0.20.1 install (ES 8.18.3 listening on :19200):
# 1. env var — ignored, still dials :9200
ELASTIC_HOST=localhost ELASTIC_PORT=19200 ./bin/softclient4es -c "CREATE TABLE emp (...)"
❌ Error: IO error during indexExists: Connection refused
# 2. conf/application.conf edited to `port = 19200` — ignored, still dials :9200
./bin/softclient4es -c "CREATE TABLE emp (...)"
❌ Error: IO error during indexExists: Connection refused
# 3. command-line flag — works
./bin/softclient4es -p 19200 -c "CREATE TABLE emp (...)"
✅ Success
This makes the shipped conf/application.conf and its documented ${?ELASTIC_HOST} / ${?ELASTIC_PORT} overrides dead configuration: the installer writes the file, the launcher passes -Dconfig.file=$CONFIG_FILE, and nothing reads either.
Root cause — two independent defects
(a) CLI defaults always beat the config. Cli.parseArgs (core/src/main/scala/app/softnetwork/elastic/client/Cli.scala:68-156) initialises scheme = "http", host = "localhost", port = 9200 as plain local vars and always emits them into CliConfig. CliConfig.elasticConfig (CliConfig.scala:34-52) then renders those values into a config string and makes it the primary config:
lazy val elasticConfig: Config = ConfigFactory
.parseString(elasticConfigAsString) // always contains scheme/host/port
.withFallback(ConfigFactory.load("softnetwork-elastic.conf"))
withFallback only supplies keys the primary config lacks — and the primary config never lacks host/port, because the hardcoded defaults are baked in whether or not the user passed a flag. Any file- or env-provided value is therefore unreachable by construction.
(b) The fallback does not load application.conf anyway. ConfigFactory.load("softnetwork-elastic.conf") loads a classpath resource of that name. The -Dconfig.file system property the launcher sets is only honoured by the no-arg ConfigFactory.load(), so conf/application.conf is never parsed — and with it the ${?ELASTIC_*} env substitutions that live in it, which is why the env vars are silently ignored too.
Both defects must be fixed; correcting either alone still leaves the connection settings unreachable.
Proposed fix
Make precedence explicit and conventional — CLI flag > env var > config file > built-in default:
- Track each connection setting as
Option in parseArgs, emitting a key into the config string only when the user actually passed the flag (None ⇒ omit the line, so withFallback can supply it).
- Resolve the fallback through the standard chain so
-Dconfig.file and env substitution both apply — e.g. ConfigFactory.load() (which honours config.file) with softnetwork-elastic.conf and the built-in defaults behind it.
Acceptance criteria
ELASTIC_PORT=19200 softclient4es -c "…" connects to :19200.
- Setting
port = 19200 in conf/application.conf connects to :19200.
-p 19200 still wins over both.
- With nothing set anywhere, the defaults stay
http / localhost / 9200.
- Same treatment for
scheme, host, username, password, api-key, bearer-token — all are declared in the shipped application.conf and all are affected.
Impact
Anyone deploying the REPL in a container or CI sets ELASTIC_HOST/ELASTIC_PORT (the installer's own output tells them to: "Or use environment variables (ELASTIC_HOST, ELASTIC_PORT, etc.)") and gets a silent connection-refused against localhost:9200 instead. The documented configuration surface simply does not work.
Found while verifying the 0.20.1 REPL.
Neither the
ELASTIC_*environment variables nor edits toconf/application.confhave any effect on the REPL's Elasticsearch connection. Only the command-line flags (-h,-p, …) work.Reproduced on the published 0.20.1 install (ES 8.18.3 listening on
:19200):This makes the shipped
conf/application.confand its documented${?ELASTIC_HOST}/${?ELASTIC_PORT}overrides dead configuration: the installer writes the file, the launcher passes-Dconfig.file=$CONFIG_FILE, and nothing reads either.Root cause — two independent defects
(a) CLI defaults always beat the config.
Cli.parseArgs(core/src/main/scala/app/softnetwork/elastic/client/Cli.scala:68-156) initialisesscheme = "http",host = "localhost",port = 9200as plain localvars and always emits them intoCliConfig.CliConfig.elasticConfig(CliConfig.scala:34-52) then renders those values into a config string and makes it the primary config:withFallbackonly supplies keys the primary config lacks — and the primary config never lackshost/port, because the hardcoded defaults are baked in whether or not the user passed a flag. Any file- or env-provided value is therefore unreachable by construction.(b) The fallback does not load
application.confanyway.ConfigFactory.load("softnetwork-elastic.conf")loads a classpath resource of that name. The-Dconfig.filesystem property the launcher sets is only honoured by the no-argConfigFactory.load(), soconf/application.confis never parsed — and with it the${?ELASTIC_*}env substitutions that live in it, which is why the env vars are silently ignored too.Both defects must be fixed; correcting either alone still leaves the connection settings unreachable.
Proposed fix
Make precedence explicit and conventional — CLI flag > env var > config file > built-in default:
OptioninparseArgs, emitting a key into the config string only when the user actually passed the flag (None⇒ omit the line, sowithFallbackcan supply it).-Dconfig.fileand env substitution both apply — e.g.ConfigFactory.load()(which honoursconfig.file) withsoftnetwork-elastic.confand the built-in defaults behind it.Acceptance criteria
ELASTIC_PORT=19200 softclient4es -c "…"connects to:19200.port = 19200inconf/application.confconnects to:19200.-p 19200still wins over both.http/localhost/9200.scheme,host,username,password,api-key,bearer-token— all are declared in the shippedapplication.confand all are affected.Impact
Anyone deploying the REPL in a container or CI sets
ELASTIC_HOST/ELASTIC_PORT(the installer's own output tells them to: "Or use environment variables (ELASTIC_HOST, ELASTIC_PORT, etc.)") and gets a silent connection-refused againstlocalhost:9200instead. The documented configuration surface simply does not work.Found while verifying the 0.20.1 REPL.