Threads for maxm

  1. 17

    I have become one of those boring people whose first thought is “why not just use Nix” as a response to half of the technical blog posts I see. The existence of all those other projects, package managers, runtime managers, container tooling, tools for sharable reproducible development environment, much of Docker, and much more, when taken together all point to the need for Nix (and the need for Nix to reach a critical point of ease of adoption).

    1. 28

      Well, maybe there’s a reason why nix hasn’t seen significant adoption?

      1. 10

        The Nix community has been aware of the DX pitfalls that prevented developers to be happy with the tooling.

        I’ve made https://devenv.sh to address these and make it easy for newcomers, let me know if you hit any issues.

        1. 3

          +1 for devenv. It’s boss. The only thing I think it’s truly “missing” at the moment is package versioning (correct me if I’m wrong).

          1. 2

            Love it! (As in: I haven’t had a reason to try it yet, but this is definitely the way to go!)

            1. 1

              it doesn’t appear to support using different versions of runtimes—which is the entire point of asdf/rtx in the first place. I’m not sure why I would use devenv over homebrew if I didn’t care about versions.

              1. 5

                I think the idea is a devenv per-project, not globally, like a .tool-versions file; as you say, it’d be a bit of a non-sequitor otherwise

                1. 2

                  Devenv, just like Nix, support that OOTB. You simply define different shell per project.

                1. 6

                  Primarily the bad taste the lacking UX and documentation leaves in people’s mouths. Python development is especially crap with Nix, even if you’re using dream2nix or mach-nix or poetry2nix or whatever2nix. Technically, Nix is awesome and this is the kind of thing the Nix package manager excels at.

                  1. 2

                    I’ve found mach-nix[1] very usable! I’m not primarily working with Python though.

                    [1] https://github.com/DavHau/mach-nix

                  2. 5

                    Yes, it’s way too hard to learn!

                    1. 6

                      because the documentation is horrible, the UX is bad, and it doesn’t like it when you try to do something outside of it’s bounds. It also solves different problems from containers (well, there’s some overlap, but a networking model is not part of Nix).

                    2. 12

                      I’ll adopt Nix the moment that the cure hurts less than the disease. If someone gave Nix the same UX as Rtx or Asdf, people would flock to it. Instead it has the UX of a tire fire (but with more copy-paste from people’s blogs) and a street team that mostly alienates 3/4 of the nerds who encounter it.

                      1. 7

                        Curious did you try https://denvenv.sh yet?

                        1. 4

                          https://devenv.sh for those clicking…

                          1. 3

                            No, thanks for the link! This looks like a real usability improvement. I don’t know if I am in the target audience, but I could see this being very useful for reproducing env in QA.

                        2. 10

                          It’s like using kubernetes. Apparently it’s great if you can figure out how to use it.

                          I’ve given up twice trying to use nix personally. I think it’s just for people smarter than me.

                          1. 7

                            Heh, that’s a good counterpoint. I would say, unlike with k8s I get very immediate benefits from even superficial nix use. (I do use k8s too, but only because I work with people who know it very well.) I assure you (honest) I’m not very smart. I just focus on using nix in the simplest way possible that gives me daily value, and add a little something every few months or so. I still have a long way to go!

                            The How it works section of the rtx README sounds very much like nix + direnv! (And of course, I’m not saying there’s no place for tools like rtx, looks like a great project!)

                          2. 4

                            Nix is another solution that treats the symptoms but not the disease. I used asdf (and now rtx) mainly for Python because somehow Python devs find it acceptable to break backwards compatibility between minor versions. Therefore, some libraries define min and max supported interpreter version.

                            Still, I’d rather use rtx than nix. Better documentation and UX than anything Nix community created since 2003.

                            1. 4

                              It’s clearly out of scope for Nix (or adf, rtx…) to fix the practices of the Python community?

                              1. 1

                                Sure. It’s good that a better alternative for asdf exists, although it would be better that such a program wasn’t needed at all.

                            2. 4

                              Isn’t it somewhat difficult to pin collections of different versions of software for different directories with Nix?

                              1. 7

                                Yes it is difficult. Nix is great at “give me Rust” but not as great at “give me Rust 1.64.0”. That said for Rust itself there aren’t third party repos that provide such capability.

                                1. 5

                                  I think you meant s/aren’t/are :)

                                  1. 2

                                    Correct. Bad typo. :)

                                  2. 4

                                    I think you are pointing out that nixpkgs tends to only ship a single version of the Rust compiler. While nixpkgs is a big component of the Nix ecosystem, Nix itself has no limitations prevent using it to install multiple version of Rust.

                                    1. 4

                                      Obviously nix itself has no limitation as I mentioned there are other projects to enable this capability. While you are correct I was referring to nixpkgs, for all intents nixpkgs is part of the nix ecosystem. Without nixpkgs, very few people would be using or talking about nix.

                                    2. 3

                                      I thought that was the point of Nix, that different packages could use their own versions of dependencies. Was I misunderstanding?

                                      1. 5

                                        What Adam means here is that depending on what revision of Nixpkgs you pull in, you will only be able to choose one version of rustc. (We only package one version of rustc, the latest stable, at any given time.)

                                        Of course, that doesn’t stop you from mixing and matching packages from different Nixpkgs versions, they’re just… not the easiest thing to find if you want to be given a specific package version.

                                        (Though for Rust specifically, as Adam mentioned, there are two projects that are able to do this easier: rust-overlay and Fenix.)

                                        1. 3

                                          This is a great tool to find a revision of Nixpkgs that has a specific version of some package that you need: https://lazamar.co.uk/nix-versions/

                                          That said, it’s too hard, and flakes provides much nicer DX.

                                          1. 3

                                            for Rust specifically, […] there are two projects that are able to do this easier: rust-overlay and Fenix

                                            The original https://github.com/mozilla/nixpkgs-mozilla still works too, as far as I know. I use it, including to have multiple versions of Rust.

                                            1. 3

                                              Alright, thanks!

                                        2. 3

                                          No I wouldn’t say so, especially using flakes. (It gets trickier if you want to use nix to pin all the libs used by a project. It’s not complicated in theory, but there are different and often multiple solutions per language.)

                                        3. 2

                                          Any pointers on how I can accomplish the same functionality of asdf in Nix?

                                          1. 8
                                            nix-shell -p nodejs-18_x jq
                                            nodejs -v
                                            jq --version
                                            

                                            Docs https://nixos.org/guides/declarative-and-reproducible-developer-environments.html or use https://devenv.sh/

                                            1. 3

                                              These are some quick ways to get started:

                                              Without flakes: https://nix.dev/tutorials/ad-hoc-developer-environments#ad-hoc-envs

                                              With flakes: https://zero-to-nix.com/start/nix-develop

                                              And add direnv to get automatic activation of environment-per-directory: https://determinate.systems/posts/nix-direnv

                                              Or try devenv: https://devenv.sh/

                                              (Pros: much easier to get started. Cons: very new, doesn’t yet allow you to pick all old versions of a language, for example.)

                                            2. 1

                                              I have become one of those boring people who just downloads an installer and double clicks it.

                                            1. 2

                                              If you are going to the trouble to make your setup more complex than a file in disk, I don’t see why you’d want to live with the limitations of sqlite.

                                              1. 1

                                                In my mental model all these tricks are so that:

                                                • Your process has the experience of having a single file on disk.
                                                • You can leverage fancy tricks to make rolling deploys and streaming backups work.

                                                Some of these efforts are heading towards full MVCC and distributed systems, which seems a bit excessive, I agree, but I think more typically they’re just trying to add some magic to make ephemeral cloud environments work with sqlite more like they did when you were just ssh-ing into some single server you leased.

                                              1. 2

                                                So fun. Is it possible to spoof this kind of thing?

                                                1. 4

                                                  So far nobody has publicly claimed to have successfully done so, and this security stackexchange post makes me think it’s probably too hard to do!

                                                1. 3

                                                  Nix works pretty well for the most part, the real friction of it is when you need to do something that doesn’t have its own package/flake or derivation. Though things like Home Manager do definitely help with this as you can easily manage your own derivatives and flakes with it, though most new users won’t actually know Home Manager exists and that’s a sort of community contention point.

                                                  1. 2

                                                    I’ve found it to be pretty easy if it’s similar enough to something that you can copy-paste the derivation. As a bonus, it’s way more natural to do than it is to package a .deb or whatever. The downside is that if you need to do anything weird, you’re in for a bad time.

                                                    1. 1

                                                      Yeah, I agree with that. The second you need to do anything out of the normal it becomes a hellscape.

                                                      1. 1

                                                        I’ve considered trying Guix because I think Nix is really held back by its language and design (why are packages functions????) but I don’t want to go from a niche distro to an even more niche one. :/

                                                        (I also worry about the fact that it’s a GNU project, but that’s a separate issue.)

                                                        1. 1

                                                          Guix is so weird.. it’s like they took nix but decided to make it only-free and use a lisp for configs

                                                          1. 1

                                                            I know there’s a “nonguix” package list somewhere… but you’re not allowed to talk about it on official Guix channels, even if someone asks, because it’s a GNU project.

                                                            TBH I don’t really care about the choice of language that much, I just really think Nix is a bad language and the heavy use of functions and lazy evaluation was a really bad idea.

                                                            1. 2

                                                              There are other places to ask things both something like this or just tele/irc/discord/matrix channels that are not the official one.

                                                              Here’s how you reproduce your system, first: guix describe --format=channels > channels.scm then: guix pull -C channels.scm and do guix system/home reconfigure..

                                                              The channel system is simpler than what nix does, it’s just a thin wrapper around git afaik. Guix cares a lot about bootstrapping so keeping track of the version of guix that is compatible with your package is possible. In nix when they update the code then old derivations may no longer build… so much for reproducibility.

                                                              Guix may actually cure some of the GNU stigma (finally!) … it feels like it is on the doorstep of the future.

                                                              1. 1

                                                                TBH I still find “we don’t talk about nonguix” annoying. I have similar frustrations with emacs and the fact that you have to explicitly add melpa. Though to be fair, it’s worse with emacs because almost all the really good emacs packages are on MELPA, whereas you could probably get by fine without nonguix as long as your hardware supports it.

                                                                Does guix describe include information about the list of installed packages or whatever? It looks to me like it’s just the state of all your channels, which is good, but it’s not what I would call a reproduction.

                                                                Overall though Guix looks very interesting, and I think more work in the space of declarative package managers/operating systems is always welcome! I look forward to seeing what comes out of it :)

                                                                1. 1

                                                                  Look, I agree that it is kind of annoying to be treated like a child and placing a flat ban on relevant technical discussion but there’s not really an issue.. Guix is free software and there is plenty of alternative (communication) channels where everyone hangs out (so they are just as “official” in many ways). You can easily make your own (guix) channel and package whatever you want (or work with others and establish communal channels like guixrus or nonguix).

                                                                  Does guix describe include information about the list of installed packages or whatever? It looks to me like it’s just the state of all your channels, which is good, but it’s not what I would call a reproduction.

                                                                  No, that is what your config is for. Guix describe is there to tell you the state of guix itself, which is (clearly) also an element in reproducing the desired software.

                                                                  1. 1

                                                                    Aha. Yeah, it’s frustrating to me that Nix doesn’t reify the channel state nicely by default. I think this is one of the things that flakes solve: when I look at /etc/nixos/flake.lock I get a list of the git hashes of each flake I have and such. I think that the version of nix I use is determined by what the nixpkgs flake gives me, but I’m not sure.

                                                              2. 1

                                                                Yeah that’s fair enough, I don’t have anything against Lisps, but just feels weird to use them as a config.

                                                                1. 1

                                                                  I’ve only ever used a lisp for emacs config, and it’s actually been alright. You can write some pretty compact DSLs using quoting.

                                                          2. 1

                                                            The second you need to do anything out of the normal it becomes a hellscape.

                                                            But that’s pretty much also true if you just want to build anything ‘out of the normal’ just once. On the plus side, nixpkgs has the largest set of packages out there.

                                                            1. 1

                                                              Well I mean more of trying to make a derivation for anything out of the norm it’s a bit of a hellscape. Say for example I want to create a derivation for some programming language I’m making so I can update it via nix, it’s a bit of a hellscape compared to something like arch’s BUILDPKG where it’s practically just a glorified shell script.

                                                              1. 2

                                                                From my point of view, a nix derivation is a glorified shell script and some meta data.

                                                                1. 1

                                                                  I mean sure, but considering PKGBUILD is literally just a shell script with variables and functions lol

                                                        2. 1

                                                          What is Home Manager and how does it help? The docs seem pretty focused on how to use it and not what it is helping with: https://nix-community.github.io/home-manager/

                                                          1. 2

                                                            Think of it like a nix system configuration but for your specific user/home directory. It allows you to have a nix config for stuff like per-user SSH keys, derivations, packages per-user, etc. Pretty handy when you have a multi-user system and want each user to be able to have their dotfile configuration in a similar format to /etc/nixos/configuration.nix.

                                                            1. 1

                                                              Very cool, thank you

                                                        1. 2

                                                          Just image, zx plus something like this: https://github.com/kellyjonbrazil/jc and you could get typescript types for bash commands.

                                                          Pair it with something like Nix to ensure that you’re using a version of the executable that matches the expected output format and you’d be off to the races.

                                                          I agree with @peter that I mostly just write bash-style scripts in higher-level languages these days, but I wonder if zx plus good editor tooling and typescript type hints would be a very productive combo.

                                                          1. 17

                                                            Keyword arguments

                                                            If these are important, you can get a pretty good approximation with structs, e.g.

                                                            type Point struct{ X, Y float64 }
                                                            func EuclidianDistance(p Point) float64 { ... }
                                                            d := EuclidianDistance(Point{X: 3.0, Y: 9.6})
                                                            

                                                            Absence of sum/discriminated-union types

                                                            Because reposurgeon events can have any one of a set of types (Blob, Tag, Commit, Callout, Passthrough, Reset) I found myself writing a lot of stupid boilerplate code like this:

                                                            Yeah, type assertions usually aren’t the way to go, as they’re not runtime-safe. Generics will make this better but the best approach right now is a non-discriminated union type e.g.

                                                            type Event struct {
                                                                *Blob
                                                                *Tag
                                                                *Commit
                                                                ...
                                                            }
                                                            

                                                            and checking for nils.

                                                            Catchable exceptions require silly contortions

                                                            Go does not have exceptions. Using panic/recover as ersatz exceptions is essentially always a design error. If you ever find yourself trying to recreate an exception control flow, you are almost certainly on a bad path.

                                                            I kept looking at places where existing nonlocal control transfers in Python could be replaced by explicit Go-style passing upwards of an error status. But I noticed that there were a significant percentage of cases in which doing this made the code more difficult to follow rather than easier . . . An early reviewer pointed out that if the Go code were an entire function it could be expressed something like this . . . That’s still a lot of eyeball friction compared to functional-style with exceptions. And it gets worse faster as the number of stages rises.

                                                            Effective use of Go requires the programmer to choose to see that example function as wonderfully explicit, rather than full of “eyeball friction”. I’ve said this many times: in Go, the happy path is equally important to the sad path. You have to just accept that and work with it, or you’ll hate the language.

                                                            Figuring out how to do type-safe polymorphism in the event list

                                                            Go does not currently support type polymorphism. Generics will change that, but at the moment, trying to approximate it is a sure-fire way to frustrate yourself and produce bad code.

                                                            1. Keyword arguments should be added to the language.

                                                            Emphatically no.

                                                            1. True iterators are felt by their absence and would be easy to add.

                                                            Not without sacrificing the performance goals of the language.

                                                            1. Any enlargement in the range of what can be declared const would be good for safety and expressiveness.

                                                            +1.

                                                            1. Yes, throw()/catch() needs to be writeable in the language. Two minimal relaxations of compilation rules would make writing it possible.

                                                            Emphatically no. One of Go’s fundamental opinions is that exceptions are bad. You have to buy in to that. If you don’t want to, no problem! Go isn’t the language for you.

                                                            1. A technical writer with an outside-in view of the language should be hired on to do an edit pass and reorganization of the documents.

                                                            +1.

                                                            1. Lookbehinds should be added to the regexp library.

                                                            There are good reasons this doesn’t exist currently. It’s unlikely to change.

                                                            1. 20

                                                              There are good reasons this doesn’t exist currently.

                                                              This comment is Go community in the nutshell. How many people were happy about lack of generics, and now they are happy about their future presence. Same think can happen here.

                                                              1. 11

                                                                Every week, I get another bullshit security notification from Depndabot that my JavaScript depends in some tangential way on a package with a malicious regex vulnerability. If people want lookbehinds, whatever, but it shouldn’t be in the standard library because it’s a total safety hazard.

                                                                1. 8

                                                                  Package regexp implements RE2 which does not support backtracking or lookbehind (are they the same thing? I’m not totally sure) by design. Adding support for these capabilities would mean switching to a different flavor of regexp, which would break the compatibility guarantee.

                                                                  There are several third-party PCRE modules available, though I’m not sure what compromises they may make.

                                                                  1. 4

                                                                    Context for the Regexp limitations: https://swtch.com/~rsc/regexp/regexp1.html

                                                                    1. 4

                                                                      Insightful comment. Thank you.

                                                                      Not without sacrificing the performance goals of the language.

                                                                      Is that true? I don’t necessarily agree that Go should get iterators, but I can’t wrap my head around why they would make Go lose performance. Couldn’t it be as simple as having range accept an interface like: (yes yes, no generics in the language, but range is special)

                                                                      interface Iter<T> {
                                                                          Next(): *T, bool
                                                                      }
                                                                      
                                                                      1. 1

                                                                        I’m unclear why no one has brought up channels and go routines as an alternative to iterators. Channel size of 1, blocks on send making it “lazy”.

                                                                        1. 4

                                                                          The linked blog post in the original post does go into that: https://ewencp.org/blog/golang-iterators/index.html

                                                                          1. 2

                                                                            Blargh! you are correct. I overlooked that when I read the post. :)

                                                                          2. 3

                                                                            They’re too slow. See a recent issue discussion: https://github.com/golang/go/issues/48567

                                                                        2. 2

                                                                          Broadly I agree, but I’m a little confused on this point:

                                                                          Not without sacrificing the performance goals of the language.

                                                                          Iterators should be zero cost, no? Is your concern that the next() method won’t be properly inlined? I also haven’t been following the generics saga closely, but presumably we’ll have typesafe generic iterators either via the stdlib or third-party once generics land.

                                                                          1. 1

                                                                            My guess is that the go compiler isn’t optimizing hard enough. Until very recently, it didn’t even put arguments in registers when calling functions.

                                                                            Rust made iterators “zero-cost”, but the real cost is in compilation time, and Go would rather sacrifice runtime speed for compilation speed.

                                                                            1. 1

                                                                              Go definitely has an inliner, but it probably can’t see across package boundaries to your point. Personally I would like to know more about the compilation cost of link-time optimizations which would optimize across compilation units.

                                                                              1. 1

                                                                                It does indeed inline across package boundaries.

                                                                                1. 1

                                                                                  Really? I thought packages were compilation units and Go didn’t do LTO (I assume LTO is required for optimizing across compilation units, but I’m pretty out of my depth)?

                                                                                  1. 1

                                                                                    I’m no expert, but here’s a basic example https://godbolt.org/z/98xKj1dfc

                                                                                    1. 1

                                                                                      Hmm, I’m suspicious about stdlib packages because I know they are treated specially by the compiler, at least in some cases.

                                                                                      1. 1

                                                                                        I believe you’re referring to compiler intrinsics. But from what I’ve seen, the compiler doesn’t special case inlining rules for the stdlib.

                                                                              2. 1

                                                                                It is not true that Go prefers to sacrifice runtime speed for compilation speed. Rather, compilation speed is a first order priority of the language, and compiler optimizations are made only when people decide to make them. The lack of a register based calling convention, for example, didn’t become the lowest hanging performance fruit until relatively recently, so effort was spent elsewhere. That’s fine.

                                                                          1. 1

                                                                            Anyone have any ideas why this essentially-static site is making Firefox run at about 15 fps? I poked around in the inspector a little but the site doesn’t actually seem to be doing anything insane. Firefox 94.0 for reference.

                                                                            1. 1

                                                                              SVG animation most likely. The image subtly sparkles. Might be more of a CPU hit than I expected :/

                                                                              1. 1

                                                                                Hmmm, blocking the header image gets it to 20 fps but performance is still weirdly slow. It’s most noticeable when swapping tabs. ¯\(ツ)

                                                                            1. 1

                                                                              Hi maxm, I got pointed to this from the Racket community. I’d love to exchange research with you because I’m working on a project that has a lot of overlap with Bramble. It’s called Denxi. It’s already capable of replicating functionality in Guix, such that the end-user is entirely responsible for the manufacture of entire functional package managers, dumb download clients, or arbitrary data workflows on their systems.

                                                                              I’m now working on a bootstrap path through GNU Mes to the Racket CS VM in particular. You can find the current code on GitHub under zyrolasting/denxi

                                                                              This isn’t meant to be a self-plug, but as an invitation to collaborate. If you have any questions about solving certain data distribution problems while you work on Bramble, please reach out. I want to see more tools like this in the space, because I believe they are better for end-user freedoms. Hopefully I can adapt some of my code to save you some time if our licenses are compatible.

                                                                              1. 1

                                                                                Very interesting, thanks for reaching out. Emailing you now.

                                                                              1. 1

                                                                                Any plans to support any of the BSDs? Specifically, FreeBSD?

                                                                                1. 2

                                                                                  Yep! That’s next after macOS, although not sure how/if I can get rootless to work on FreeBSD/BSDs.

                                                                                1. 3

                                                                                  Very nice! I’m a NixOS and Guix user/contributor, but recognize they both have approachability/learning curve issues.

                                                                                  I think Skylark is a great choice: its similarity to Python will make it much more approachable.

                                                                                  Would using Bazel directly have been possible? That is, have a Bazel rule that returns a derivation. This would have benefitted from the existing Bazel features:

                                                                                  • remote caching
                                                                                  • external dependencies
                                                                                  • sandboxing
                                                                                  • Bazel’s build rules, e.g. https://github.com/bazelbuild/rules_go . I hear Nix plans to support source-file level building, and this would allow you to jump to that in one go.

                                                                                  Perhaps there’s some downsides I’m missing?

                                                                                  1. 1

                                                                                    Interesting, possibly. I did want this use case “bramble run github.com/maxmcd/busybox ash”. Running arbitrary programs from remote sources. Not sure if I could have gotten that with Bazel, or how I would have made it work.

                                                                                    I will say that to my understanding (which could be wrong) in Bazel:

                                                                                    • Builders can touch anything in the operating system.
                                                                                    • Dynamic dependencies are not supported (more here and elsewhere).

                                                                                    I wanted Bramble to be able to support custom builders without having to trust them to not mess with things on the host machine. I don’t know a ton about Bazel custom builders, but I think they require some level of trust. When similar language-specific builders are added to Bramble they will be able to define custom build logic without having access to the host machine. This was part of the goal, lower barrier to entry to contribute custom builders, both in terms of complexity and trust. Would be interested in your thoughts on this one, might have it wrong.

                                                                                    I’d also really like to support dynamic dependencies with Bramble, some references to that here. This work is ongoing, but I think there’s a path.

                                                                                    On a more honest note, I wanted to build a thing, and didn’t spend as much time auditing nix/guix/bazel, so could have missed a more harmonious opportunity. I do also wonder if building a Starlark frontend for Nix was something else I should have explored.

                                                                                    1. 2

                                                                                      I will say that to my understanding (which could be wrong) in Bazel:

                                                                                      • Builders can touch anything in the operating system.
                                                                                      • Dynamic dependencies are not supported (more here and elsewhere).

                                                                                      https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf compares build systems and finds (see Table 1) that Bazel internally supports dynamic dependencies:

                                                                                      ``As of writing, it is not possible to express dynamic dependencies in user-defined build rules; however some of the pre-defined build rules require dynamic dependencies and the internal build engine can cope with them''
                                                                                      

                                                                                      Though as you point out, the Bazel developers don’t think that’s possible: https://github.com/bazelbuild/bazel/issues/2362#issuecomment-273508287.

                                                                                      I wanted Bramble to be able to support custom builders without having to trust them to not mess with things on the host machine. I don’t know a ton about Bazel custom builders, but I think they require some level of trust. When similar language-specific builders are added to Bramble they will be able to define custom build logic without having access to the host machine. This was part of the goal, lower barrier to entry to contribute custom builders, both in terms of complexity and trust. Would be interested in your thoughts on this one, might have it wrong.

                                                                                      I don’t know much about Bazel’s sandboxing internals.

                                                                                      On a more honest note, I wanted to build a thing, and didn’t spend as much time auditing nix/guix/bazel, so could have missed a more harmonious opportunity. I do also wonder if building a Starlark frontend for Nix was something else I should have explored.

                                                                                      That’s fair enough. It’s great that you’ve created this and shared it. The ecosystem benefits from different approaches.

                                                                                  1. 3

                                                                                    This is super cool! I spent a few months building something similar (in Go, using Starlark). I would get hung up on bootstrapping various low-level dependencies and eventually gave up. I’m excited to check this out and maybe contribute. :)

                                                                                    1. 3

                                                                                      No way! Yeah, navigating bootstrapping dependencies was (and continues to be) very tough. I would stop working on the project at times solely because of how intimidated I was about getting some random build working. Imagine it will get easier, but I feel you. And yes please check it out and contribute :)

                                                                                    1. 10

                                                                                      Always love to see tools in this area. Can you give a more detailed overview of the goals of the project, and how it differs from Bazel or Nix, or how it will differ when finished?

                                                                                      1. 15

                                                                                        Goals are basically:

                                                                                        • Take lessons from Rust and Go tooling and bring them to build systems. Automatic documentation generation, tests in docs, good ergonomics and documentation, incredible error messages, etc..
                                                                                        • Packages are a community effort like language libraries, no core package tree. This creates security concerns, but I think assuming packages are hostile is better for the long-term security of the system.
                                                                                        • Limit as much as possible. No network access (where possible), no arbitrary build inputs, no complex command line flags, be very cautious with feature additions, get to API stability quickly. Strong limitations have helped with some of the projects features and I also want to take the “runs on my computer” problem very seriously.

                                                                                        Not very different from Bazel. Should enable similar workflows (once mature tooling is built for all languages :| ), just with more sharing, better cache availability. Bazel is hard to use because you have to do a lot of explicit dependency definition, I’m excited about some ways to make that easier with Bramble, but we’ll see if they pan out.

                                                                                        The builds work like Nix, but it’s not an OS, and it’s not many of the other things Nix is. I hope to solve some of the pain points of Nix, specifically around approachability and documentation.

                                                                                        Nix also expects libraries to do a lot of code generation to support language dependencies. Like go2nix, cargo2nix, etc… I think first-class support for this workflow is very important, and don’t want to leave it to third party libs to generate Bramble code. Will be trying to support Go and Go modules first to see if I can arrive at patterns that are a little more user friendly. Basically I want the user interface to be this:

                                                                                        def build():
                                                                                            return go_build(files["./"])
                                                                                        

                                                                                        And then you go.mod and go.sum are parsed under the hood, their resolution is persisted without code generation, all while maintaining reproducibility guarantees. There is work to be done here, but I think I have a rough plan. You’re welcome to check out these notes for more on that strategy, although my notes are usually quite messy and hard to follow.

                                                                                        1. 3

                                                                                          Thanks! Sounds like a cool project!

                                                                                          How do you expect to see better sharing and better cache availability? I don’t properly understand how Bazel structures this, but I would expect cache sharing to be quite high, given the stress on the system inside Google.

                                                                                          1. 2

                                                                                            Bazel workspaces are pretty porous and tend to source a bunch of dependencies from the system. It’s actually quite hard to get a good coverage across machines.

                                                                                            1. 2

                                                                                              A few ways:

                                                                                              1. There will be a public build cache and public build architecture. Bramble is designed to be safe in ways that I think will make running a publicly available build server much easier.
                                                                                              2. Very limited build inputs, if you’re building something public there’s a higher chance that you’re building it in the same way as the cached build.
                                                                                              3. The sharing piece is just being hopeful about the package ecosystem. In my experience with Bazel you’re mostly working with the built-in builders, with Bramble I would hope a given project would be composing together publicly maintained build tools across hundreds of repositories. I just hope that the ecosystem is more diverse/vibrant, but that’ll take some doing.
                                                                                              1. 2

                                                                                                Ah, that sounds fantastic! If you can solve the security issues around the public build cache I can see that being a huge advancement. Honestly having the shared build cache is why Bazel works so well at Google, but naturally that doesn’t help anyone else.

                                                                                        1. 3

                                                                                          Looks interesting, thanks!

                                                                                          Very limited build inputs. No env-var, arguments, or other inputs allowed for build configuration. Almost all configuration must be done in-code.

                                                                                          “Almost”, what’s some of the remaining configuration which can (or must be) done out side of code? command line flags?

                                                                                          1. 3

                                                                                            The only planned command line flag at the moment is --target which will allow someone to specify the target architecture/os to build for. Bramble gets a lot of benefit from being that locked down, like it’s easy to just “build everything in the project” since there are no arbitrary configuration values to consider, but I wanted to be a little cautious in the post because Bramble is immature and I’m not sure what legitimate use-cases I’m preventing by having such little build input.

                                                                                          1. 2

                                                                                            I come from a more traditional understanding of the term “build system” (i.e., a system that direct compilation and linking of software) and I can’t understand from the article what exactly is Bramble building. For example, there is this sentence:

                                                                                            Here Bramble is building the necessary dependencies to run bash.

                                                                                            Does this mean that Bramble compiles and links bash and all its dependencies from C source?

                                                                                            1. 2

                                                                                              For the moment, and for convenience, that demo is just downloading an archive and using patchelf to link it to a specific libc. In the near future it should do exactly what you’re saying, all of the derivations/executables in github.com/maxmcd/bramble will build from source. The intention is get rough feature parity with something like Bazel or Nix and have good tooling to compile various programs in various languages from source.

                                                                                              That being said, since Bramble is just a system and users can define whatever build rules they want, there’s nothing stopping other users from running builds that are really just archive downloads.

                                                                                            1. 2

                                                                                              Thank you for contributing isolated package management technology.

                                                                                              Could direnv invoke Bramble? I heavily rely on direnv’s ability to use nix and incant nix-shell automatically.

                                                                                              1. 1

                                                                                                It’s a little awkward to run programs at the moment. I think you could put a “bramble.toml” in your home directory to define dependencies for all sub-folders and then alias various invocations of “bramble run” to do what you’re saying. However the “bramble run” functionality is very new and there are some limitations.

                                                                                                This is definitely a workflow I hope to support, but a few things need to be worked out.

                                                                                                I think that would eventually work very well though, since you’d be able to define the sanbox rules for every program and only give things access to what they need to run.

                                                                                              1. 7

                                                                                                Hey, that’s actually not a bad tip (I’m not 100% sure it’s worthy of its own post, but it’s definitely not worth flagging). My main concern is:

                                                                                                None of the viruses in npm are able to run on my host when I do things this way.

                                                                                                This is assuming a lot of the security of docker. Obviously, it’s better than running npm as the same user that has access to your SSH/Azure/AWS/GCP/PGP keys / X11 socket, but docker security isn’t 100%, and I wouldn’t rely on it fully. At the end of the day, you’re still running untrusted code; containers aren’t a panacea, and the simplest misconfiguration can render privilege escalation trivial.

                                                                                                1. 3

                                                                                                  the simplest misconfiguration can render privilege escalation trivial.

                                                                                                  I’m a bit curious which configuration that’d be?

                                                                                                  1. 2

                                                                                                    not OP, but “--privileged” would do it. or many of the “--cap-add” options

                                                                                                    1. 1

                                                                                                      Not 100% sure here but lots of containers are configured to run as root, and file permissions are just on your disk right? so a container environment lets you basically take control of all mounted volumes and do whatever you want.

                                                                                                      This is of course only relevant to the mounted volume in that case, though.

                                                                                                      I think there’s also a lot of advice in dockerland which is the unfortunate intersection of easier than all alternatives yet very insecure (like most ways to connect to a github private repo from within a container involves some form of exposing your private keys).

                                                                                                    2. 1

                                                                                                      This is assuming a lot of the security of docker

                                                                                                      Which has IMO a good track record. Are there actually documented large scale exploits of privilege escalation from a container in this context? Or at all?

                                                                                                      Unless you’re doing stupid stuff I don’t think there’s a serious threat with using Docker for this use case.

                                                                                                    1. 2

                                                                                                      Worth noting:

                                                                                                      1. Once you run this you’ll have a node_modules folder on the host system, if this system is not linux you might run into issues using those modules on the host (ie: if there are os-specific binary artifacts).
                                                                                                      2. Running npm install through a bind mount “-v $(pwd):/pwd” on macOS will see a significant performance hit since every file will be sent from the VM to the host. It’s usually better to keep the node_modules and any files used for the build inside the container. Then just mount an output directory for any build outputs and copy them into the output folder at the end of the build.
                                                                                                      1. 3

                                                                                                        Congrats on writing a Dockerfile.

                                                                                                        A few suggestions:

                                                                                                        • Specify which Debian you want. Latest will change.
                                                                                                        • apt-get update && apt-get install -y nodejs npm
                                                                                                          • Doing this in three steps is inefficient and can cause problems.
                                                                                                        1. 6

                                                                                                          Even better, don’t write a Dockerfile at all. Use one of the existing official Node images which allow you to both specify what Debian and what Node version you want.

                                                                                                          1. 1

                                                                                                            I tried this but I didn’t get a shell, it would be nice to get it working.

                                                                                                            1. 4

                                                                                                              Those images have node set as the CMD, which means it will open the node REPL instead of a shell. You can either do docker run -it node:16-buster-slim /bin/bash to execute bash (or another shell of your choice) instead, or you can make a Dockerfile using the node image as your FROM and add an ENTRYPOINT or CMD instead to eliminate the need to invoke the shell.

                                                                                                              1. 3

                                                                                                                Incidentally, to follow up as I remembered to write this, one reason that it’s common for images to use CMD in this way is that it makes it easier to use docker run as sort-of-drop-in replacements of uncontained CLI tools.

                                                                                                                With an appropriate WORKDIR set, you can do stuff like

                                                                                                                alias docker-node='docker run --rm -v $PWD:/pwd -it my-node-container node

                                                                                                                alias docker-npm='docker run --rm -v $PWD:/pwd -it my-node-container npm

                                                                                                                and you’d be able to use them just like they were node/npm commands restricted to the current directory, more or less. It wouldn’t preserve stuff like cache and config between runs, though.

                                                                                                            2. 1

                                                                                                              I have to agree with this. I tend toward “OS” docker images (debian and ubuntu usually) for most things because installing dependencies via apt is just too damn convenient. But for something like a node app, all of your (important) deps are coming from npm anyway so you might as well use the images that were made for this exact use case.

                                                                                                            3. 3

                                                                                                              what problems?

                                                                                                              1. 3

                                                                                                                It creates 3 layers instead of one. You can only have 127 layers in a given docker image so it’s good to combine multiple RUN statements into one where practical.

                                                                                                                1. 3

                                                                                                                  Also the 3 layers will take unnecessary space. You can follow the docker best practices and remove the cache files and apt lists afterwards - that will ensure your container doesn’t have to carry them at all.

                                                                                                                2. 2

                                                                                                                  Check out the apt-get section in the best practice guide: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

                                                                                                              1. 4

                                                                                                                does anyone know what application was used to draw those wonderful diagrams?

                                                                                                                1. 4
                                                                                                                1. 2

                                                                                                                  Not trying to critique this package, I think this is fun and helped me understand generics a little bit more.

                                                                                                                  But on that topic: Is this meaningfully using generics? I though any was an alias for interface{}. Does this do type checking beyond what the non-generic version would do?

                                                                                                                  1. 5

                                                                                                                    Is this meaningfully using generics? I though any was an alias for interface{}. Does this do type checking beyond what the non-generic version would do?

                                                                                                                    I tried to write a non-generic version of this package first, but you can’t reflect on an interface type. When you do reflect.Value(x), you lose the fact that x was, e.g. an error, because reflect.Value only takes interface{}. You’d end up saying whether the underlying concrete type was nil or not, which is typically not what you want. To work around that, you could require that everything is passed as a pointer, e.g. reflect.Value(&err), but that kind of sucks as an API. If you look at what truthy.Value does, it accepts a value of type T, and then passes &T to reflect.Value and calls value.Elem() to get the correct type. So yes, on a technical level, you couldn’t quite make this API work without generics, although it could be close.

                                                                                                                    Then there’s truthy.First. To be honest, truthy.First is the only part of the package that I consider actually useful, and even that, I mostly expect it to be used for picking a string or default. Anyhow, that requires generics to avoid the cast back from interface type to the concrete type. However, if you wanted to, you could write truthy.SetDefault with Go 1.17 reflection alone, since that takes a pointer. truthy.Filter is also pretty easy to do with pre-generic Go, but the code would be a lot uglier.

                                                                                                                    1. 2

                                                                                                                      Ah interesting. Thank you for the detailed response.

                                                                                                                    2. 1

                                                                                                                      afaics not, no.