Stream: contributing

Topic: basic-cli Windows 10/11 support


view this post on Zulip Luke Boswell (Oct 12 2026 at 04:42):

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.

view this post on Zulip Luke Boswell (Oct 12 2026 at 04:44):

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).

view this post on Zulip Aurélien Geron (Oct 12 2026 at 04:47):

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.

view this post on Zulip Luke Boswell (Oct 12 2026 at 06:56):

For anyone interested WIP here https://github.com/roc-lang/basic-cli/pull/512

view this post on Zulip notramo (Oct 12 2026 at 12:43):

I think supporting old systems with known unpatched critical security vulnerabilities is a bad idea, especially if it adds complexity to the project.

view this post on Zulip notramo (Oct 12 2026 at 12:43):

There are many great rolling-release Linux distros to switch to, for completely free.

view this post on Zulip Luke Boswell (Oct 12 2026 at 22:41):

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.

view this post on Zulip Luke Boswell (Oct 12 2026 at 22:52):

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:

view this post on Zulip Luke Boswell (Oct 13 2026 at 02:22):

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

view this post on Zulip Luke Boswell (Oct 13 2026 at 03:48):

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

view this post on Zulip Luke Boswell (Oct 13 2026 at 03:57):

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.

view this post on Zulip Aurélien Geron (Oct 13 2026 at 05:59):

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!.

view this post on Zulip Luke Boswell (Oct 13 2026 at 06:28):

https://github.com/roc-lang/roc/blob/51b76ca1098158f4004cd88d65bf98808ba8518c/docs/langref/loops.md?plain=1#L98

view this post on Zulip Luke Boswell (Oct 13 2026 at 08:26):

https://github.com/roc-lang/basic-cli/releases/tag/0.26.0-rc2

view this post on Zulip Jonathan (Oct 13 2026 at 09:59):

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.

view this post on Zulip notramo (Oct 13 2026 at 11:29):

@Luke Boswell, the .exec() call replaces the current process in most languages. If it's not the case, what about calling it .run() instead?

view this post on Zulip notramo (Oct 13 2026 at 11:31):

Oh, I see there's also a .run().

view this post on Zulip notramo (Oct 13 2026 at 11:31):

Maybe a .run_simple()?

view this post on Zulip Anton (Oct 13 2026 at 17:49):

EndOfInput occurring feels normal and expected so I don't love it being an Err everywhere.

view this post on Zulip Anton (Oct 13 2026 at 18:10):

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!()

view this post on Zulip Anton (Oct 13 2026 at 18:13):

All the rest looks excellent :)

view this post on Zulip Anton (Oct 13 2026 at 18:21):

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.

view this post on Zulip Zak Kohler (Oct 13 2026 at 19:53):

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:

  1. Catching SIGINT/SIGTERM. For example 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.)
  2. Unix socket connect (+1 #500): UnixSocket.connect!(path, ...) with the same API as Tcp.Connection. listen! would be a bonus.
  3. File permissions: a mode on create (e.g. open_writer! with { mode: 0o600 }, so secrets are never world-readable) and a chmod (Path.set_permissions!).
  4. Symlinks: Path.symlink!(target, link) and Path.read_link!(path).
  5. Http redirects: document that 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.
  6. (minor) A 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.

view this post on Zulip Luke Boswell (Oct 14 2026 at 01:19):

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.

view this post on Zulip notramo (Oct 14 2026 at 10:49):

notramo what about call! instead of exec! -- 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.

view this post on Zulip notramo (Oct 14 2026 at 10:53):

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.

view this post on Zulip notramo (Oct 14 2026 at 11:04):

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 ?

view this post on Zulip notramo (Oct 14 2026 at 11:07):

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