Does anyone have a concern if basic-cli for assumes Windows 10/11 which bundles UCRT? that would mean older versions of windows isn't supported using the release.
I'm trying to upgrade the platform so users don't need to have Visual C++ redistributable tools installed and I need a suitably licensed runtime (Windows doesn't permit redistribution of the .lib's -- but you can link into an exectuable).
Legacy Windows (7, 8, 8.1, or XP) represent ~0.8% of Windows users. I think it's low enough to ignore: not even Microsoft supports them.
For anyone interested WIP here https://github.com/roc-lang/basic-cli/pull/512
I think supporting old systems with known unpatched critical security vulnerabilities is a bad idea, especially if it adds complexity to the project.
There are many great rolling-release Linux distros to switch to, for completely free.
Does anyone have any priority basic-cli bugs or requests we could add before this next release? I'm just making some fixes now and preparing another release later to unblock this Windows work and can probably add other patches in if we need anything.
I made the mistake of asking Claude to check against the latest langref and builtins which have improved a lot since we last cleaned things up :sweat_smile:
This was the outcome of asking that question... https://github.com/roc-lang/basic-cli/issues?q=is%3Aissue+state%3Aopen+%23517+%23518+%23519+%23520+%23521+%23522+%23523+%23524+%23525+%23526+%23527
Here are the draft release notes for the next upgrade to 0.26
https://github.com/roc-lang/basic-cli/blob/9ae735e946a0de756b32afed8d5b4e0c12ea1118/docs/releases/0.26.0.md
I've made a lot of assumptions and some opinionated calls so I can get it all integrated quickly and put out a RC to test it. There are a few breaking changes in here, but I think they are all worthwhile doing and I've included the reasoning in each linked GH issue.
I would appreciate any feedback from people using it or upgrading from 0.25.0 (and earlier) - both positive and negative.
The biggest motivation here was just to give the basic-cli API some love now that many things have matured and the compiler is more stable. I felt like the API was still holding back a little due to old bugs and workarounds that no longer need to be there, and we've also learn't a lot in the last 6 months building out other platforms and new builtins etc that we can roll back into this.
It looks good to me!
Quick question: in the release notes, you use for! (which I didn't even know existed): I suppose this is used when the loop is effectful? If so, why does the "Write files piece by piece" section use for instead of for!?
Edit: oh got it, for! is for streams. Can't find the docs for for!.
https://github.com/roc-lang/basic-cli/releases/tag/0.26.0-rc2
Luke Boswell said:
Here are the draft release notes for the next upgrade to 0.26
https://github.com/roc-lang/basic-cli/blob/9ae735e946a0de756b32afed8d5b4e0c12ea1118/docs/releases/0.26.0.md
Looks great! Many of these things I have until now been doing in Roc (file iter, wrapping module errors, time types, time comparison) and it has been interesting to work without transactions, so good to have them now. The decoder change is also nice, so that you don't have to handle the cols and stmt.
${…}in a query is a bound parameter
I'm a little torn on this - I quite liked having the query string be static with all the bindings listed in one place.
@Luke Boswell, the .exec() call replaces the current process in most languages. If it's not the case, what about calling it .run() instead?
Oh, I see there's also a .run().
Maybe a .run_simple()?
EndOfInput occurring feels normal and expected so I don't love it being an Err everywhere.
Losing this compact way to execute commands is unfortunate for scripts:
basic-cli 0.25:
Cmd.exec!("cargo", ["build"])
basic-cli 0.26:
Cmd.new("cargo").args(["build"]).exec!()
All the rest looks excellent :)
One more thing, exec_exit_code!() was it's own function because it allowed you to avoid recording stdout and stderr to memory. That can be large perf benefit in some cases.
This is claude's summary for porting our Roux dragrace site's cli ops:
Thanks for asking! I checked our tooling (a ~10k-line Go ops tool we want to port to Roc) against 0.26.0-rc2, and it covers nearly everything. Our asks, in priority order:
Signal.catch!([Interrupt, Terminate]) plus a way to poll for pending signals, with blocking waits (Duration.sleep!, Child.read!, Tcp reads with a timeout) returning Err(Interrupted). Today Ctrl-C or systemctl stop kills the process before it can clean up; for us that means cloud VMs left running and billing. No issue exists yet, and I'm happy to file one. (Notes: it would have to work alongside the Tty handlers in src/tty.rs, and sleep! currently restarts after a signal.)UnixSocket.connect!(path, ...) with the same API as Tcp.Connection. listen! would be a bonus.open_writer! with { mode: 0o600 }, so secrets are never world-readable) and a chmod (Path.set_permissions!).Path.symlink!(target, link) and Path.read_link!(path).Http.send! doesn't follow them (GitHub release downloads always 302, and Http.get! then fails as a JSON error). Maybe an opt-in max_redirects.StdoutMode that sends the child's stdout to the parent's stderr, the mirror of MergeWithStdout.Only #1 has no workaround from inside the program. The others are conveniences we can shell out around.
Thanks all for the ideas and feedback. I appreciate it.
@Jonathan I'm also not sure about the query parameters in the string literals. It's interesting that it's not a Str, but a typed Query value which gives us that compile time safety and error messages. We still have Query.new("… WHERE id IN (?, ?)", ids) for a runtime style builder.
@notramo what about call! instead of exec! -- this matches Haskell and Python's use of that term
@Anton there's definitely a small trade-off there, but I thought it was acceptable. Also you can still avoid recording stout and error to memory using cmd.inherit_stdio().run!()?.status
@Zak Kohler I'll need to look into catching SIGINT/SIGTERM. For things like sockets and symlinks it's more complicated because we target Windows too. Currently all the effects work identically cross-platform I think. max_redirects and file permissions or sounds reasonable I guess. I'm not super familiar with permissions on Windows but we can give it a go.
notramo what about
call!instead ofexec!-- this matches Haskell and Python's use of that term
That seems good. The goal is to avoid reusing the exec name with a different behavior from C function or the shell command. By the way, I recommend adding an actual exec! function, which replaces the current process with the new executable, just like as C function or the shell command does it. It can be a useful tool for scripting in Roc.
For things like sockets and symlinks it's more complicated because we target Windows too.
Windows has Unix socket support, but it's somewhat limited (e.g. no datagram, no file descriptor passing). However, even a common subset would be very useful. I.e. connect to a stream socket and write data into it. It's the most common use case for sockets.
Of course, full support for platform-specific things would be nice too.
What about trying out the idea described by Richard here: #beginners > ✔ OS-specific APIs in platforms ?
And a possible improvement on that idea: instead of exposing a separate struct with platform-specific functions in one place, maybe expose all functions separately, e.g. UnixSocket.datagram_open which returns a tag union, but it does not carry a struct, it just carries a single function, e.g. [Unsupported, Supported(Path -> DatagramSocket)]
Last updated: Oct 15 2026 at 17:56 UTC