fix: pass loaded WflConfig to Interpreter (not just timeout) - #466
Conversation
Interpreter::with_timeout() constructs a fresh WflConfig with only
the timeout field set, defaulting everything else. That silently
discards every setting loaded from .wflcfg other than timeout_seconds
-- including web_server_bind_address, so `listen on port N as ...`
always binds 127.0.0.1 regardless of the operator-supplied value.
Switch to Interpreter::with_config(Arc::new(config.clone())) so the
fully-loaded config reaches the interpreter. The .clone() is needed
because `config` is also used later at main.rs:1297 (`if config.logging_enabled`).
Repro before this change:
echo "web_server_bind_address = 0.0.0.0" > .wflcfg
cat > server.wfl <<EOF
listen on port 8080 as s
main loop:
wait for request comes in on s as r
respond to r with "hi" and content_type "text/plain"
end loop
EOF
cargo run --release -- server.wfl &
ss -tlnp | grep :8080
# LISTEN 0 128 127.0.0.1:8080 <- should be 0.0.0.0:8080
After this change the same repro binds 0.0.0.0:8080 as intended.
📝 WalkthroughWalkthroughThe interpreter initialization in script execution was refactored to pass the full Arc-wrapped configuration object to Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/main.rs (2)
1183-1183: Nit: importArcinstead of fully qualifying.Minor readability tweak — the rest of the file uses plain imports at the top, so bringing
Arcinto scope would be more consistent.♻️ Proposed refactor
@@ -1,6 +1,7 @@ use std::env; use std::fs; use std::io::{self, Write}; use std::path::Path; use std::process; +use std::sync::Arc; use std::time::Instant;- let mut interpreter = Interpreter::with_config(std::sync::Arc::new(config.clone())); + let mut interpreter = Interpreter::with_config(Arc::new(config.clone()));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.rs` at line 1183, The line creating the interpreter uses a fully qualified std::sync::Arc; import Arc at the top instead and replace std::sync::Arc::new(config.clone()) with Arc::new(config.clone()) to match the file's import style—update the top-of-file use statements to include Arc and keep the Interpreter::with_config(...) call otherwise unchanged.
1183-1183: LGTM — full config now reaches the Interpreter.The fix correctly replaces
with_timeout(config.timeout_seconds)withwith_config(Arc::new(config.clone())), so fields likeweb_server_bind_address,subprocess_config, andshell_execution_modefrom.wflcfgare now honored on the script-execution path. Theclone()is necessary becauseconfigis read later in this function.One behavior change to note: the old
with_timeoutpath clampedtimeout_secondsto 300s, whereaswith_configuses the configured value verbatim. This impact is isolated to script execution (the REPL atsrc/repl.rs:42still useswith_timeoutand retains the 300s cap). This is the intended outcome—the cap was a side effect of the limitedwith_timeoutshortcut.Optional: Import
Arcfromstd::syncat the top of the file rather than using the fully qualified path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.rs` at line 1183, The Interpreter is now constructed with Interpreter::with_config(std::sync::Arc::new(config.clone())), which is correct; to tidy imports, add use std::sync::Arc at the top and change the construction to Interpreter::with_config(Arc::new(config.clone())); keep the clone() as config is used later and do not reintroduce the old with_timeout call or the 300s cap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/main.rs`:
- Line 1183: The line creating the interpreter uses a fully qualified
std::sync::Arc; import Arc at the top instead and replace
std::sync::Arc::new(config.clone()) with Arc::new(config.clone()) to match the
file's import style—update the top-of-file use statements to include Arc and
keep the Interpreter::with_config(...) call otherwise unchanged.
- Line 1183: The Interpreter is now constructed with
Interpreter::with_config(std::sync::Arc::new(config.clone())), which is correct;
to tidy imports, add use std::sync::Arc at the top and change the construction
to Interpreter::with_config(Arc::new(config.clone())); keep the clone() as
config is used later and do not reintroduce the old with_timeout call or the
300s cap.
There was a problem hiding this comment.
Pull request overview
Fixes the script-run startup path to pass the fully loaded .wflcfg (WflConfig) into the interpreter, so runtime settings like web_server_bind_address are actually honored during execution.
Changes:
- Replace
Interpreter::with_timeout(config.timeout_seconds)withInterpreter::with_config(Arc<WflConfig>)in the main script execution path.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| exec_trace!("Starting execution of script: {}", &file_path); | ||
|
|
||
| let mut interpreter = Interpreter::with_timeout(config.timeout_seconds); | ||
| let mut interpreter = Interpreter::with_config(std::sync::Arc::new(config.clone())); |
There was a problem hiding this comment.
Switching from Interpreter::with_timeout(...) to Interpreter::with_config(...) changes timeout semantics: with_timeout clamps the timeout to a max of 300s, but with_config uses config.timeout_seconds as-is. If the 300s cap is intentional (e.g., to prevent extremely long-running scripts), consider preserving it here by clamping config.timeout_seconds before constructing the interpreter or by moving the cap into config loading/validation so both constructors behave consistently.
| let mut interpreter = Interpreter::with_config(std::sync::Arc::new(config.clone())); | |
| let mut interpreter_config = config.clone(); | |
| interpreter_config.timeout_seconds = interpreter_config.timeout_seconds.min(300); | |
| let mut interpreter = | |
| Interpreter::with_config(std::sync::Arc::new(interpreter_config)); |
* fix: pass loaded WflConfig to interpreter so .wflcfg bind address applies (#466) The script-run path in `src/main.rs` built the interpreter with `Interpreter::with_timeout(config.timeout_seconds)`, which copies only the timeout out of the loaded `.wflcfg` and resets every other field to `WflConfig::default()`. The most visible symptom was that `web_server_bind_address` was silently ignored: `listen on port N` always bound `127.0.0.1`, making WFL web servers loopback-only in any deployment that relied on `.wflcfg` to change the bind address. Switch to `Interpreter::with_config(Arc::new(config.clone()))`, which already reads the full configuration. The clone is needed because `config` is read again later in `main.rs`. `with_timeout` is unchanged and still used by the REPL and tests, so there is no API surface change. Adds `tests/web_server_bind_address_cli_test.rs`, an end-to-end regression test that launches the compiled binary against a real `.wflcfg` and asserts the configured bind address reaches the listening socket (verified to fail on the pre-fix code). Includes a Dev Diary entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012t3m4FA8XJ49E2smwyFzRp * fix: address PR review — preserve 300s timeout cap, use OS-assigned test ports Two findings from the Devin review on PR #601: 1. The switch to `Interpreter::with_config` dropped the 300-second execution timeout cap that `with_timeout` enforced, so a `.wflcfg` `timeout_seconds` above 300 would run uncapped. Re-apply the same clamp in the script-run path before building the interpreter, keeping this change purely additive (config fields now propagate; timeout semantics unchanged). The cap never affected web servers — `check_time` skips the timeout inside a main loop. 2. The new CLI test used hardcoded ports (8471–8473), which risk flaky failures under parallel CI. Replace them with OS-assigned free ports via a `free_port` helper that binds an ephemeral port and reads it back. Dev Diary updated to document the preserved cap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012t3m4FA8XJ49E2smwyFzRp --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Interpreter::with_timeout(config.timeout_seconds)insrc/main.rsconstructs a freshWflConfigwith only the timeout field copied across — every other setting falls backto
WflConfig::default(). This means.wflcfgis parsed correctly, but almostnothing from it actually reaches the interpreter.
The most user-visible symptom is that
web_server_bind_addressis silently ignored:no matter what operators put in
.wflcfg,listen on port N as ...binds127.0.0.1because that's the hard-coded default. WFL web servers are effectively loopback-only
in any production deployment that relies on
.wflcfgto change the bind address.Interpreter::with_config(Arc<WflConfig>)already exists — it's just not being calledfrom
main.rsin the script-run path. This PR switches to it.Patch
The
.clone()is required becauseconfigis also used later inmain.rsatif config.logging_enabled(around line 1297). Without it you getE0382: use of moved value.Repro
Before this change:
After this change the same repro binds
0.0.0.0:8080as configured.Context
Found while deploying WFL 26.4.1 to host a public splash page on a cloud VPS — the
server daemon insisted on loopback-only and the config file had no effect. Traced to
with_timeoutdiscarding everything. Patched locally, site came up, PR'd upstream.Notes for review
with_timeoutstill exists and is still used elsewhere (REPL, tests, etc.), sono API surface changes.
.clone()is negligible —WflConfigis a small structand this happens once at startup.
than "which config the interpreter runs with".
Summary by CodeRabbit