Most active commenters
  • pxc(4)
  • est(3)
  • w0m(3)

←back to thread

Fun with uv and PEP 723

(www.cottongeeks.com)
618 points deepakjois | 50 comments | | HN request time: 1.303s | source | bottom
1. ACAVJW4H ◴[] No.44370555[source]
finally feels like Python scripts can Just Work™ without a virtualenv scavenger hunt.

Now if only someone could do the same for shell scripts. Packaging, dependency management, and reproducibility in shell land are still stuck in the Stone Ages. Right now it’s still curl | bash and hope for the best, or a README with 12 manual steps and three missing dependencies.

Sure, there’s Nix... if you’ve already transcended time, space, and the Nix manual. Docker? Great, if downloading a Linux distro to run sed sounds reasonable.

There’s got to be a middle ground simple, declarative, and built for humans.

replies(16): >>44370574 #>>44370603 #>>44370609 #>>44370667 #>>44370757 #>>44370943 #>>44371081 #>>44371402 #>>44372258 #>>44372278 #>>44372472 #>>44372942 #>>44373775 #>>44374090 #>>44374399 #>>44376192 #
2. fouronnes3 ◴[] No.44370574[source]
Consider porting your shell scripts to Python? The language is vastly superior and subprocess.check_call is not so bad.
3. SmellTheGlove ◴[] No.44370603[source]
Would homebrew do the job?
replies(1): >>44379466 #
4. ndr ◴[] No.44370609[source]
Why bother writing new shell scripts?

If you're allowed to install any deps go with uv, it'll do the rest.

I'm also kinda in love with https://babashka.org/ check it out if you like Clojure.

5. bigstrat2003 ◴[] No.44370667[source]
> Packaging, dependency management, and reproducibility in shell land are still stuck in the Stone Ages.

IMO it should stay that way, because any script that needs those things is way past the point where shell is a reasonable choice. Shell scripts should be small, 20 lines or so. The language just plain sucks too much to make it worth using for anything bigger.

replies(4): >>44372677 #>>44374216 #>>44374396 #>>44375053 #
6. bjackman ◴[] No.44370757[source]
For the specific case of solving shell script dependencies, Nix is actually very straightforward. Packaging a script is a writeShellApplication call and calling it is a `nix run`.

I guess the issue is just that nobody has documented how to do that one specific thing so you can only learn this technique by trying to learn Nix as a whole.

So perhaps the thing you're envisaging could just be a wrapper for this Nix logic.

7. pxc ◴[] No.44370943[source]
I use Nix for this with resholve and I like it a lot.
8. traverseda ◴[] No.44371081[source]
I don't think nix is that hard for this particular use case. Installing nix on other distros is pretty easy, and once it's installed you just do something like this

    #! /usr/bin/env nix-shell
    #! nix-shell -i bash -p imagemagick cowsay

    # scale image by 50%
    convert "$1" -scale 50% "$1.s50.jpg" &&
    cowsay "done $1.q50.jpg"
Sure all of nixos and packaging for nix is a challenge, but just using it for a shell script is not too bad
replies(2): >>44375926 #>>44376976 #
9. password4321 ◴[] No.44371402[source]
I'm unable to resist responding that clearly the solution is to run Nix in Docker as your shell since packaging, dependency management, and reproducibility will be at theoretical maximum.
10. wazzaps ◴[] No.44372258[source]
Check out mise: https://mise.jdx.dev/

We use it at $work to manage dev envs and its much easier than Docker and Nix.

It also installs things in parallel, which is a huge bonus over plain Dockerfiles

replies(1): >>44374994 #
11. andenacitelli ◴[] No.44372278[source]
+1 for Mise, it has just totally solved the 1..N problem for us and made it hilariously easy to be more consistent across local dev and workflows
12. wpm ◴[] No.44372472[source]
I simply do not write shell scripts that use or reference binaries/libraries that are no pre-installed on the target OS (which is the correct target, writing shell scripts for portability is silly).

There is no package manager that is going to make a shell script I write for macOS work on Linux if that script uses commands that only exist on macOS.

replies(1): >>44374690 #
13. pxc ◴[] No.44372677[source]
When you solve the dependency management issue for shell scripts, you can also use newer language features because you can ship a newer interpreter the same way you ship whatever external dependencies you have. You don't have to limit yourself to what is POSIX, etc. Depending on how you solve it, you may even be able to switch to a newer shell with a nicer language. (And doing so may solve it for you; since PowerShell, newer shells often come with a dependency management layer.)

> any script that needs those things

It's not really a matter of needing those things, necessarily. Once you have them, you're welcome to write scripts in a cleaner, more convenient way. For instance, all of my shell scripts used by colleagues at work just use GNU coreutils regardless of what platform they're on. Instead of worrying about differences in how sed behaves with certain flags, on different platforms, I simply write everything for GNU sed and it Just Works™. Do those scripts need such a thing? Not necessarily. Is it nicer to write free of constraints like that? Yes!

Same thing for just choosing commands with nicer interfaces, or more unified syntax... Use p7zip for handling all your archives so there's only one interface to think about. Make heavy use of `jq` (a great language) for dealing with structured data. Don't worry about reading input from a file and then writing back to it in the same pipeline; just throw in `sponge` from moreutils.

> The language just plain sucks too much

There really isn't anything better for invoking external programs. Everything else is way clunkier. Maybe that's okay, but when I've rewritten large-ish shell scripts in other languages, I often found myself annoyed with the new language. What used to be a 20-line shell script can easily end up being 400 lines in a "real" language.

I kind of agree with you, of course. POSIX-ish shells have too much syntax and at the same time not enough power. But what I really want is a better shell language, not to use some interpreted non-shell language in their place.

replies(2): >>44372862 #>>44373420 #
14. MatmaRex ◴[] No.44372862{3}[source]
Broke: Dependency management used for shell scripts

Woke: Dependency management used for installing an interpreter for a better programming language to write your script in it

Bespoke: Dependency management used for installing your script

15. nothrabannosir ◴[] No.44372942[source]
Nix is overkill for any of the things it can do. Writing a simple portable script is no exception.

But: it’s the same skill set for every one of those things. This is why it’s an investment worth making IMO. If you’re only going to ever use it for one single thing, it’s not worth it. But once you’ve learned it you’ll be able to leverage it everywhere.

Python scripts with or without dependencies, uv or no uv (through the excellent uv2nix which I can’t plug enough, no affiliation), bash scripts with any dependencies you want, etc. suddenly it’s your choice and you can actually choose the right tool for the job.

Not trying to derail the thread but it feels germane in this context. All these little packaging problems go away with Nix, and are replaced by one single giant problem XD

replies(1): >>44379577 #
16. m2f2 ◴[] No.44373420{3}[source]
Nice, if only you could count on having it installed on your fleet, and your fleet is 100pct Linux, no AIX, no HPUX, no SOLARIS, no SUSE on IBM Power....

Been there, tried to, got a huge slap in the face.

replies(1): >>44374117 #
17. est ◴[] No.44373775[source]
> finally feels like Python scripts can Just Work™ without a virtualenv scavenger hunt.

Hmm, last time I checked, uv installs into ~/.local/share/uv/python/cpython-3.xx and can not be installed globally e.g. inside a minimal docker without any other python.

So basically it still runs in a venv.

replies(1): >>44375166 #
18. Narushia ◴[] No.44374090[source]
> Great, if downloading a Linux distro to run sed sounds reasonable.

There's a reason why distroless images exist. :)

19. kstrauser ◴[] No.44374117{4}[source]
Been there, done that. I am so glad I don’t have to deal with all that insanity anymore. In the build farm I was responsible for, I was always happy to work on the Linux and BSD boxes. AIX and HPUX made me want to throw things. At least the Itanium junk acted like a normal server, just a painfully slow one.

I will never voluntarily run a bunch of non-Linux/BSD servers again.

replies(1): >>44381293 #
20. xavdid ◴[] No.44374216[source]
My rule of thumb is that as soon as I write a conditional, it's time to upgrade bash to Python/Node/etc. I shouldn't have to search for the nuances of `if` statements every time I need to write them.
replies(3): >>44379432 #>>44380173 #>>44384656 #
21. johnisgood ◴[] No.44374396[source]
I have a couple of projects consisting of around >1k lines of Bash. :) Not to bloat, but it is pretty easy to read and maintain. It is complete as well. I tested all of its functionalities and it just works(tm). Were it another language, it may have been more than just around 1k LOC, however, or more difficult to maintain. I call some external programs a lot, so I stick'd to a shell script.
22. yard2010 ◴[] No.44374399[source]
That's a shame as I got to a monk-level python jujitsu. I can fix any problem, you name it, https nightmare, brew version vs pyenv, virtualenv shenanigans. Now all this knowledge is a bad investment of time.
replies(1): >>44378175 #
23. fragmede ◴[] No.44374690[source]
fwiw (home)brew exists on both platforms
24. KingMob ◴[] No.44374994[source]
I declared nix bankruptcy earlier this year and moved to mise. It does 90% of what I need for only 1% of the effort of nix.
25. maccard ◴[] No.44375053[source]
Unfortunately there’s basically no guarantee that even the simplest scripts work.

    #!/bin/bash
    make $1
Has multiple possible problems with it.
26. ndr ◴[] No.44375166[source]
https://docs.astral.sh/uv/reference/settings/#pip_system
replies(1): >>44375707 #
27. est ◴[] No.44375707{3}[source]
I mean how to install `uv python install` into system-wide.

No matter what I tried it's always a symlink into ~/.local

replies(1): >>44391388 #
28. caspar ◴[] No.44375926[source]
Last time I checked,[0] this works great - as long as you don't particularly care which specific versions of imagemagick or cowsay you want.

If you do care, then welcome to learning about niv, flakes, etc.

[0]: admittedly 3 years ago or so.

replies(1): >>44380944 #
29. db48x ◴[] No.44376192[source]
Guix is easier to grok than Nix, if anyone is looking to save themselves some effort.
30. Imustaskforhelp ◴[] No.44376976[source]
I will say this with a whole heart. My arch linux broke and I wanted to try out nix.

The most shocking part about nix is the nix-shell (I know I can use it in other distros but hear me out once), its literally so cool to install projects for one off.

Want to record a desktop? Its one of those tasks that for me I do just quite infrequently and I don't like how in arch, I had to update my system with obs as a dependency always or I had to uninstall it. Ephemerality was a concept that I was looking for before nix since I always like to try out new software/keep my home system kind of minimalist-ish Cool. nix-shell -p obs-studio & obs and you got this.

honestly, I like a lot of things about nix tbh. I still haven't gone too much into the flake sides of things and just use it imperatively sadly but I found out that nix builds are sandboxed so I found a unique idea of using it as a sandbox to run code on reddit and I think I am going to do something cool with it. (building something like codapi , codapi's creator is kinda cool if you are reading this mate, I'd love talking to ya)

And I personally almost feel as if some software could truly be made plug n play (like imagine hetzner having nix os machines (currently I have heard that its support is finnicky) but then somehow a way to get hetzner nix os machines and then I almost feel as if we can get something really really close to digital ocean droplets/ plug n play without any isolation that docker provides because I guess docker has its own usecases but I almost feel as if managing docker stuff is kinda harder than nix stuff but feel free to correct me as I am just saying what I am feelin using nix.

I also wish if something like functional lua (does fxn lua exist??) -> nix transpiler because I'd like to write lua instead of nix to manage my system but I guess nix is fine too!

replies(2): >>44377105 #>>44379302 #
31. Hetzner_OL ◴[] No.44377105{3}[source]
Hi there, Since you mentioned Hetzner, I thought I would respond here. While we do not have NixOS as one of our standard images for our cloud products, it is part of our ISO library. Customers can install it manually. To do this, create a cloud server, click on it, and then on the "ISO" in the menu, and then look for it listed alphabetically. --Katie
replies(2): >>44377407 #>>44380916 #
32. Imustaskforhelp ◴[] No.44377407{4}[source]
Hey hetzner. I am just a 16 year old boy (technically I am turning 17 on 2nd july haha but I want nothing from ya haha) who has heard great things about your service while being affordable but never have tried them because I guess I just don't have a credit card/I guess I am a really frugal person at this moment haha. I was just reading one of your own documents if I feel correct and it said that the support isn't the best(but I guess I was wrong)

I guess I will try out nix on hetzner for sure one day. This is really cool!!! Thanks! I didn't expect you to respond. This is really really cool. You made my day to whoever responded with this. THANKS A LOT KATIE. LOTS OF LOVE TO HETZNER. MAY YOU BE THE WAY YOU ARE, SINCE Y'ALL ARE PERFECT.

replies(1): >>44377857 #
33. Hetzner_OL ◴[] No.44377857{5}[source]
Hi again, I'm happy that I made your day! You seem pretty easy to please if that is all it takes. Keep in mind that customers must be 18 years old. I believe that is a legal requirement here in Germany, where we are based. Until then, if you're a fan, maybe you'd enjoy seeing what we're up to. We're on YouTube, reddit, Mastodon, Instagram, Facebook, and X. --Katie
34. arcanemachiner ◴[] No.44378175[source]
Never say never.

Knowing the Python packaging ecosystem, uv could very well be replaced by something else. It feels different this time, but we won't know for a while yet.

replies(1): >>44379454 #
35. Purplish9893 ◴[] No.44379302{3}[source]
If you think nix-shell is cool, try out comma. https://github.com/nix-community/comma

When there's some random little utility I need I don't always bother to install it. It's just `, weirdlittleutil`.

36. w0m ◴[] No.44379432{3}[source]
Historically; my rule of thumb is as soon as I can't see the ~entire script without scrolling - time to rewrite in Python/ansible. I Think about the rewrite, but it usually takes awhile to do it (if ever)
37. w0m ◴[] No.44379454{3}[source]
Agreed. I migrated ~all my personal things to Uv; but I'm sure once I start adopting widely at work I'll find edge cases you need to know the weeds to figureout/work around.
38. w0m ◴[] No.44379466[source]
Homebrew does a great job @ initial setup; it does a poor job of keeping a system clean and updated over time.
39. exe34 ◴[] No.44379577[source]
> Nix is overkill for any of the things it can do. Writing a simple portable script is no exception.

ChatGPT writes pretty good nix now. You can simply paste any errors in and it will fix it.

40. pxc ◴[] No.44380173{3}[source]
What nuances are there to if statements, exactly?

An if statement in, for instance bash, just runs any command and then runs one of two blocks of code based on the exit status of that command. If the exit status is truthy, it runs what follows the `then`. If it's falsey, it rhns what follows the `else`. (`elsif` is admittedly gratuitous syntax— it would be better if it were just implemented as an if inside an else statement.) This seems quite similar to other programming languages and like not very much to remember.

I'll admit that one thing I do in my shell scripts is avoid "fake syntax"— I never use `[` or `[[` because these obscure the real structure of the statements for the sake of cuteness. I just write `test`, which makes clear that it's just an ordinary command, ans also signals to someone who isn't sure what it's doing that they can find out just by running `man test`, `help test`, `info test`, etc., from the same shell.

I also agree that if statements and if expressions should be kept few and simple. But in some ways it's actually easier to do this in shell languages than in many others! Chaining && and/or || can often get you through a substantial script without any if statements at all, let alone nested ones.

replies(2): >>44382934 #>>44396166 #
41. loremm ◴[] No.44380916{4}[source]
and I've been using nixos on hetzner, nothing crazy but it's always worked great :-). A nice combination with terraform
42. loremm ◴[] No.44380944{3}[source]
This is a hack but I still found it helpful. If you do want to force a certain version, without worrying about flakes [1] this can be your bash shebang, with similar for nix configuration.nix or nix-shell interactive. It just tells nix to use a specific git hash for it's base instead of whatever your normal channel is.

For my use case, most things I don't mind tracking mainline, but some things I want to fix (chromium is very large, python changes a lot, or some version broke things)

``` #! nix-shell -i bash -p "cowsay" '(import (fetchTarball { url="https://github.com/NixOS/nixpkgs/archive/eb090f7b923b1226e8b... sha256 = "15iglsr7h3s435a04313xddah8vds815i9lajcc923s4yl54aj4j";}) {}).python3' ```

[1] flakes really aren't bad either, especially if you think about it as just doing above, but automatically

43. lenerdenator ◴[] No.44381293{5}[source]
I honestly don't get why there are still a bunch of non-Linux/BSD servers, at least if the goal is to do UNIX-y stuff.

I haven't touched AIX or HPUX in probably a decade and I thought they were a weird idea back then: proprietary UNIX? Is it still 1993?

replies(1): >>44382244 #
44. kstrauser ◴[] No.44382244{6}[source]
At the time (10 years ago) I worked for a company with enormous customers who had all kinds of different deployment targets. I bet that list is a lot shorter today.

I hope so, for their sake. shudder

45. xavdid ◴[] No.44382934{4}[source]
I mean, there are 3 equally valid ways to write an if statement: `test`, `[`, and `[[`. In the case of the latter two, there are a mess of single-letter flags to test things about a file or condition[0]. I'm not sure what makes them "fake syntax", but I also don't know that much about bash.

It's all reasonable enough if you go and look it up, but the script immediately becomes harder to reason about. Conditionals shouldn't be this hard.

[0]: https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.ht...

replies(1): >>44383927 #
46. pxc ◴[] No.44383927{5}[source]
You don't need any of those to write an if statement. I frequently write if statements like this one

    if ! grep -qF something /etc/some/config/file 2>/dev/null; then
      do_something
    fi
The `test` command is there if you want to use it, but it's just another command.

In the case of Bash, `test` is a built-in command rather than an external program, and it also has two other names, `[` and `[[`. I don't like the latter two because they look, to a naive reader, like special syntax built into the shell— like something the parser sees as unique and different and bear a special relationship to if-statements— but they aren't and they don't. And in fact you can use them in other shells that don't have them as built-ins, if you implement them as external commands. (You can probably find a binary called `[` on your system right now.)

(Actually, it looks like `[[` is even worse than "fake syntax"... it's real special syntax. It changes how Bash interprets `&&` and `||`. Yikes.)

But if you don't like `test`, you don't have to use it; you can use any command you like!

For instance, you might use `expr`:

  if expr "1 > 0"; then
    echo this will always run
  else
    echo this will never run
  fi
Fish has some built-ins that fall into a similar niche that are handy for simple comparisons like this, namely `math` and `string`, but there are probably others.

If you really don't like `test`, don't even need to use it for checking the existence or type (dir, symlink, socket, etc.) of files! You can use GNU `find` for that, or even sharkdp's `fd` if you ache for something new and shiny.

Fish actually has something really nice here in the `path` built-in, which includes long options like you and I both wish `test` had. You can write:

  if path -q --type=dir a/b/c
    touch a/b/c/some-file
  end
You don't need `test` for asking about or asserting equality of variables, either;

  grep -qxF "$A" <<< "$B"
is equivalent to

  test "A" = "$B"
or with the Fish `string` built-in

  string match --entire $A $B
The key is that in a shell, all commands are truthy in terms of their exit status. `&&` and `||` let you combine those exit statuses in exactly the way you'd expect, as do the (imo much more elegant) `and` and `or` combiner commands in Fish.

Finally, there's no need to use the likes of `test` for combining conditions. I certainly never do. You can just write

  test "$A" = "$B" && test "$C" = "$D"
instead of something like

  [ "$A" = "$B" -a "$C" = "$D" ]


If-statements in shell languages are so simple that there's practically nothing to them. They just take a single command (any!) and branch based on its exit status! That's it.

As for readability: any program in any language is difficult to understand if you don't know the interfaces or behaviors of the functions it invokes. `[`/`test` is no different from any such function, although it appears that `[[` is something weirder and, imo, worse.

47. colonial ◴[] No.44384656{3}[source]
This is a decent heuristic, although (IMO) you can usually get away with ~100 lines of shell without too much headache.

Last year I wrote (really, grew like a tumor) a 2000 line Fish script to do some Podman magic. The first few hundred lines were great, since it was "just" piping data around - shell is great at that!

It then proceeded to go completely off the rails when I went full sunk cost fallacy and started abusing /dev/shm to emulate hash tables.

E: just looked at the source code. My "build system" was another Fish script that concatenated several script files together. Jeez. Never again.

48. bjornasm ◴[] No.44391388{4}[source]
>When Python is installed by uv, it will not be available globally (i.e. via the python command). Support for this feature is in preview. See Installing Python executables for details.

>You can still use uv run or create and activate a virtual environment to use python directly.

replies(1): >>44392930 #
49. est ◴[] No.44392930{5}[source]
yes that's exactly what I meant on OP's "virtualenv scavenger hunt" statement.

You still need some kind of venv, even with the power of uv.

50. passwd ◴[] No.44396166{4}[source]
The difference being, as far as I know, that `[[` is the real syntax. This from what I remember helps in avoiding certain class of issues, gives better error messages and is more certain to be a bash built-in.

What I would worry about more is that it breaks `sh` compatibility.