Rustshop Flakebox: Rust DX we can share and love

I’m building a thing:

Some background and motivation: Over last year of working professionally on a Nix and Rust using project, we’ve built a great CI, lints, polished things etc. It’s really lovely to work with.

The problem is - now I want the same experience everywhere, and I can’t be copying and constantly updating the same set of files (scripts, configs, etc.) between projects. At the core, I want the same thing in every Rust project (+/- some customizations).

So this is what I’m trying to build. You import a flake, do some minimal initial setup and it will generate and set up everything for you. With time you can customize locally and contribute the things you need upstream, so you can reuse in all your projects.

I had to finally learn NixOS’s module system and I already see it will work perfectly for what I’m trying to do. (Thank you to all helpful users of NixOS Matrix channel for help!)

Right now it’s rather bare: I only created the core frame and added 5% of what I want to have there in the end. But I think it’s worthwhile showing early.

I’m looking for feedback and help. Leave a comment here or post on the Flakebox Github Discussions forum.

7 Likes

Have you looked into https://flake.parts/? Because that would allow you to modularize the “implementation” details and share the module with other Rust projects.

This is what I did for Haskell development―via haskell-flake.

If you are already aware of flake.parts, I’m curious to know how Flakebox differs from a flake-parts based approach.

2 Likes

Other than basically composing modules, I don’t understand what the rest of flake-parts is for. perSystem seems like a different API to do same thing as flake-utils.lib.eachDefaultSystem ? I must say - it doesn’t do a good job explaining the value of yet another abstraction. Flakebox in essence uses NixOS modules under the hood as well, and user can inject both the config and other modules via argument, which should achieve the same thing.

It is of my belief that composability/exendability gets exponentially harder as the scope gets larger/more general. So I’m not sure how much I buy into some general “compose your flake from flake parts” idea, thought maybe if I had known and understood flake-parts, I could use it instead of doing my own module handling code. But I learned what goes where, so there’s at least that.

Haskell-flake seems very much like Flakebox but for Haskell, though the exact details might be different. I don’t know of any such existing tool for Rust, and I know at least one user (myself) with around 20 projects where I want a shared flake to slap on Rust project.

I don’t know much about Haskell development, so hard for me to compare otherwise. I have set of tools, lints and formatters that I want there for myself, certain level of integration, certain github workflows auto-generated, then crane + fenix integration for handling non-trivial Rust derivations that something need.

3 Likes

The main benefit to me is the uniform interface. Let’s say you have a project that uses both Rust & Haskell. Suppose one writes an internal flake-parts module for Rust, now they can import that along with haskell-flake at the top-level flake.nix and define the options in uniform manner. The flake-parts ecosystem also provides some standard modules that one can add to this project: for eg., to run services locally, or autoformat the project tree.

A Rust flake-parts module is waiting to happen (who will be the lucky person to spearhead it?). Meanwhile, I’m content with a specialized module for Leptops, which then gets used in downstream projects whose flake.nix already uses multiple modules … doing something like this without flake-parts can get … unwieldy.

Ultimately, it would be nice if flakes itself will provide a module system, possibly inspired by flake-parts design.

(CC @roberth in case if I missed anything)

3 Likes

But it’s not a unified interface. Now, for my already extra-niche project that I might forever be the only user anyway, I have to add another requirement for any other users that would like to use it, and explain why things are different then in their existing flake, and how most of the existing documentation and examples they find online are not matching.

I wholeheartedly agree. I wish this was integrated into flakes. Then it would be unified interface, and some things that looks like a duct tape for parts don’t fit well might not be necessary. If there’s an issue I can :+1: and voice support I’m happy to do so.

I really don’t like how the system is handled in flakes, but flake-utils is simple and gets the job done, without being a heavy “grand total unified framework” that one needs to buy into.

Once flake-parts are built-into Flakes or are de facto standard that everybody uses anyway, I’ll happily use them myself.

I don’t find it appealing at all. First - how many projects like that actually are there. Second without grand-unified-module-system, the user can still use Flakebox and add the Haskell specific rules and hooks if so they desire. It’s not going to be that much work, so complicating everything for some minority of users who have a decent solution seems like a bad tradeoff.

Again - I believe extensibility sounds great on paper, but breaks down quickly if used too heavily. That problem is visible in coding editor space. Over and over there are project who think “let’s build a extendable framework for text editing and then people can write plugins they need”. But in practice this leads to having to maintain 500 lines of configs just to get basic things working well, tons of engineering overhead figuring out how to let these plugins not break each other all the time, and still having to deal with weird bugs and crappy (unintegrated) user experience.

Part of the reason why I’m using Helix over Neovim. Helix has all heavy, well integrated core and most of the things built in, and once it does have extendeability via plugins I’m only going to need a handful most useful ones.

Same with Flakebox - I don’t want to solve a large category of problems, but only a small scope problem. You have a Rust project, you add one input, you’re done. If you don’t like certain detail, the module system is there to help you turn it off/change it. But it’s supposed to be an exception, not a requirement. You don’t have too look through a list of modules and enable anything.

Also - its not a framework. It’s a library. I don’t like frameworks. (framework - you give it things and it calls them, so it’s in control; library - you call and use it as you please, you’re in control). Module system in Flakebox is an implementation detail to help you customize it, not something dictating how you need to do stuff outside of what Flakebox is doing.

3 Likes

There are quite a few actually, for eg., nammayatri (“open-soure Uber”) - which uses Nix to provide Haskell environment, build Docker images, do autoformatting and linting checks, setup pre-commit hooks and (in future) run native services locally ― all of these are implemented as flake-parts modules, either in repo or upstream.

My experience indicates the obverse. If every language/tool used its own custom Nix, the resultant flake tends to get convoluted. Do you have an example project, similar in scope to nammayatri, that uses the approach you advocate - so we can see how these look in comparison?

1 Like

Unless you have some stats that show that more than 10% percent of all Flakes with Rust code in them also use Haskell (or just more languages), no anecdata is going to help.

Please don’t bother me about flake-parts anymore. I’m happy to know they exist, I’m not planning to use them right now.

That’s fine, I’m posting here not to convince you in particular - but mainly for the benefit of the community; there are others here who also read this thread and might benefit from a wider viewpoint. And you are free to not reply to my posts, of course. “no anecdata is going to help.” - this is a very dismissive comment in response to my providing very concrete examples backing up my view, and if this is how you are going to continue to engage, you may very well choose to not reply any further for it is not going to benefit anybody.

This is not a relevant point, as people use flakes for more than language support; I’ve provided examples above twice already (even your own project provides pre-commit hooks).

1 Like

I said

and you gave me two examples. Great, there are two or maybe even more. It’s just a poor argument, so how I’m supposed to be not dismissive of it. Either you can argue that projects combining languages are common, or not. These few project that want multi language support can use flake-parts for all I care.

Again:

I’m not dismissive of flake-parts, just that one argument, and my main problem is that I don’t to force my users (probably just myself in my downstream projects) to see/learn flake-parts the framework.

If there’s a way to use flake-parts for Flakebox without forcing my users to deal/know about it I’d be interested. If the users can opt-in into it, perfect. That’s their choice. If it’s possible please send links, and possibly it should be explained somewhere early in the docs, as that’s not the impression I got.

As it is right now it seems to conflate trying to rearchitect how the flakes are structured and handled with a overarching global framework which will be an uphill battle, with trying to build a common infra for dev env components. I would be interested into buying into the later.

Edit: If anyone even uses Flakebox, and they need some Haskell support, we can add it later and make the project more generalized then. I rather deal with small problems that I know how to solve well first, then move outwards from there.

As I said above, the multi-language case is a red herring. Mono-language projects too use disparate features in their flake - I’ve listed them (twice) in the comments above.

I’ll compose a separate post (maybe as a blog post) explaining my point in detail and post a link to it in this Discourse latter

We were exploring some ideas related to that in #boom-nix@matrix.org, though nothing material came out of it yet.

2 Likes

for the record, flake-parts has a year, 2 year head start as the way to modularize/organize flakes. lots of big nix projects have adapted to it or have modules for it to consume, some which are listed on the website (devenv, devshell, dream2nix, nix-cargo-integration, std)
and more. so i wouldn’t say its just yet another way, but the leader of the way thus far.

Having mentioned it, are you aware of GitHub - yusdacra/nix-cargo-integration: Library to easily and effortlessly integrate Cargo projects with Nix. ? it seems the two projects share the same goal. for instance, both use crane and provide devshells, have configurable modules. @yusdacra

3 Likes

Update: I have added cross-compilation toolchains for Android and iOS targets and some tutorials. Plus tons of smaller changes.

I’ve successfully rolled it out in our production setup to replace existing infrastructure. So far so good.

1 Like