0
14
borosilicate

@lemmy.dbzer0.com

borosilicate 2 points 10 hours ago

There aren’t that many posts here. If there were, I might be more forgiving of people posting ads. But there are none.

If it shouldn’t be considered a desperate act to sell your classes rather than engage us, why do it? You say you’re not hurting for work. You say you have to turn work away. You accuse me of not reading anything you write yet I have read every word.

I ask again: why immediately sell out your one opportunity on this platform to spread ads? Why not gain people’s trust first before switching into a cynical paywalled lesson?

path: 0 26301590 26301797 26303961 26304017 26305590, hotness: undefined, score: 2, children: 1
borosilicate 2 points 12 hours ago

It is, IMO.

They’re ads.

I understand you are desperate for work. But you really should engage people before spamming them.

path: 0 26301590 26301797 26303961, hotness: undefined, score: 2, children: 3
borosilicate 0 points 15 hours ago

Not sure why you think it’s appropriate to spam multiple communities with your ads.

path: 0 26301590, hotness: undefined, score: 0, children: 7
borosilicate 4 points a day ago

I definitely commit a lot more to my private forgejo than codeberg account anymore despite everything I do being specifically AGPL3.

path: 0 26287546, hotness: undefined, score: 4, children: 0
borosilicate 22 points 6 days ago

Checks gitignore: Claude

I would assign this a trust level comparable to dipping the tip of your penis into an open sewer in Calcutta while wearing a condom with small holes.

path: 0 26218385, hotness: undefined, score: 22, children: 2
borosilicate 63 points a month ago
path: 0 25744825, hotness: undefined, score: 63, children: 10
borosilicate 27 points a month ago
path: 0 25744825 25746704 25747187, hotness: undefined, score: 27, children: 0
borosilicate 19 points a month ago
path: 0 25750992, hotness: undefined, score: 19, children: 1
borosilicate 15 points a month ago
path: 0 25864892, hotness: undefined, score: 15, children: 1
borosilicate 12 points 24 days ago

Resident (evidently unwelcome) nix evangelist chiming in here.

The article is about scripts that declare their own dependencies. I wrote another response lamenting the fact that we are talking about dependencies without mentioning IMO the best way to handle dependencies in a script and got flamed by a toxic luddite:

NIX is the answer.

Look at what actually gets declared in the article: the library version goes in the file, and the thing that runs the file is whatever brew install hands you that second. Going through OP's examples:

Ruby: gem "optimist" has no version at all. Every machine resolves it to whatever is newest on first run.

Rust: cargo +nightly -Zscript. The +nightly syntax is a rustup proxy feature, so a distro-packaged cargo won't even parse it. Nightly is a different compiler every day, -Zscript is unstable and its frontmatter syntax has already changed once, clap = "4" and anyhow = "1" are ranges, and there's no Cargo.lock. Nothing about that script is fixed except its text.

Haskell: optparse-applicative is pinned exactly, but base >= 4.18 && < 5 accepts any GHC from 9.6 up, and the transitive deps get solved against whatever cabal update fetched. Two people running it a month apart get two build plans. You can add an index-state in a {- project: -} block, which helps with Hackage and does nothing for GHC.

Python: typer is exact, click and rich and the rest float, and >=3.10 is any interpreter. uv lock --script fixes that by writing a second file, at which point it's no longer a single-file script.

Also env -S needs coreutils 8.30+ or a BSD env, and every prerequisite listed is Homebrew on macOS.

The biggest gap is that none of the examples touches a C library. The first script that needs libpq or openssl or zlib headers is outside what NuGet, Hackage, crates.io or PyPI can describe. You're back to brew/apt and whatever version happens to be installed.

Nix has been usable as a shebang interpreter for YEARS, so here is the Haskell example with the compiler, every library, and libc pinned by one commit hash:

#!/usr/bin/env nix
#! nix shell --impure --expr ``
#! nix with (builtins.getFlake ''github:NixOS/nixpkgs/e554fab72f81915600f3f449b786fd9af40439a5'').legacyPackages.${builtins.currentSystem};
#! nix ghc.withPackages (ps: [ ps.optparse-applicative ])
#! nix ``
#! nix --command runghc

import Options.Applicative

data Options = Options
  { name :: String
  }

options :: Parser Options
options =
  Options
    <$> strArgument
      ( metavar "NAME"
     <> help "Name to greet"
      )

main :: IO ()
main = do
  opts <-
    execParser $
      info
        (options <**> helper)
        (fullDesc <> progDesc "Say hello")

  putStrLn $ "Hello, " <> name opts <> "!"

--impure is only there so it can read the current system. Same GHC and same build of every dependency on any Linux or macOS box, today or in a hundred fifty years. Native deps are the same mechanism: add postgresql to the list.

When the library isn't in nixpkgs, nix users pin the artifact by content hash. The article's babashka example downloads org.babashka/cli from Clojars at runtime. Here the jar is a fixed-output fetch handed to bb on its classpath, so nothing gets resolved when the script runs:

#!/usr/bin/env nix
#! nix shell --impure --expr ``
#! nix with (builtins.getFlake ''github:NixOS/nixpkgs/e554fab72f81915600f3f449b786fd9af40439a5'').legacyPackages.${builtins.currentSystem};
#! nix let cli = fetchurl { url = ''https://repo.clojars.org/org/babashka/cli/0.12.91/cli-0.12.91.jar''; hash = ''sha256-HPvn4scG4lHZJJLPpXV5ZqbQ17aFZ64AC4fmF5B2HHs=''; };
#! nix in runCommand ''bb-pinned'' { nativeBuildInputs = [ makeWrapper ]; } ''makeWrapper ${babashka}/bin/bb $out/bin/bb-pinned --set BABASHKA_CLASSPATH ${cli}''
#! nix ``
#! nix --command bb-pinned

(require '[babashka.cli :as cli] :reload)

(defn hello [{:keys [name]}]
  (println (str "Hello, " name "!")))

(cli/dispatch
  [{:exec-fn hello
    :args->opts [:name]
    :spec {:name {:positional true :require true :desc "Name to greet"}}}]
  *command-line-args*
  {:prog "hello" :help true})

The :reload matters: bb bundles its own copy of babashka.cli, and without it the require keeps the bundled one. If Clojars ever serves different bytes for that URL, the script refuses to run. This works for a jar with no transitive deps, which this one is. A real dependency tree needs a generated lock file, and at that point you want a flake.nix and flake.lock next to the script, with nix run replacing the shebang.

What you don't get for free just by choosing Nix: you need Nix installed (one prerequisite instead of one per language), flakes are still behind an experimental flag upstream, first run is slow, evaluation adds a few hundred ms per run, and the five-line shebang is uglier than anything in the article. And if you write nixpkgs#babashka without a rev you've pinned nothing, it follows the registry.

So I'd say it belongs in the list. It's the only entry where the interpreter is part of what the script declares.

path: 0 25907795, hotness: undefined, score: 12, children: 11
borosilicate 7 points a month ago
path: 0 25740251, hotness: undefined, score: 7, children: 0
borosilicate 6 points 25 days ago
path: 0 25891218, hotness: undefined, score: 6, children: 1
borosilicate 5 points a month ago
path: 0 25863495 25864787, hotness: undefined, score: 5, children: 4
borosilicate 4 points a month ago
path: 0 25713325 25739287, hotness: undefined, score: 4, children: 0
borosilicate 3 points 24 days ago

No problem. Thanks for being friendly.

Not RTFM territory at all! ☺️ The manual is honestly one of Nix's weaker points.

  1. No generator, I wrote those by hand. The multi-line one looks scarier than normal usage though. 95% of the time the whole thing is one line: #! nix shell nixpkgs#ripgrep nixpkgs#jq --command bash. The two ugly bits in my examples both come from a command. The commit hash is nix flake metadata github:NixOS/nixpkgs/nixos-unstable --json | jq -r .locked.rev. The sha256 for the jar I got the lazy way: put a fake hash in, run it, and Nix fails with "specified X, got Y". Paste Y in. Everybody does it that way and though it can be considered “hacky” you only have to do that dance once when you declare or want to update it to the latest hash.

For anything bigger than a script you don't write hashes at all. You write a flake.nix that says "nixpkgs, unstable branch" and Nix generates a flake.lock with the exact commit and hashes, same idea as Cargo.lock or package-lock.json.

  1. Yes, and this is the thing Nix is best at. One naming trap first: for "current stable release of the language" you want the nixos-unstable branch. "Unstable" means the package set rolls forward, not that the packages are betas. It has whatever the latest stable Go/Rust/GHC/etc is, usually within days, and it only advances after the test suite passes. The "stable" branches (nixos-26.05 and so on) freeze versions for six months, which is what you want for a server and usually not for a dev box.

A dev environment is a flake.nix in the repo root:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  outputs = { self, nixpkgs }:
    let
      systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
      forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
    in
    {
      devShells = forAllSystems (pkgs: {
        default = pkgs.mkShell {
          packages = [
            pkgs.go
            pkgs.gopls
            pkgs.ripgrep
          ];
        };
      });
    };
}

nix develop drops you into a shell with exactly those. First run writes flake.lock, you commit it, and everyone who clones the repo gets identical versions without having to download a bloated Docker image (instead running those dependencies natively in Nix’s sandbox). Updating is nix flake update and then commit the lock. Nothing moves until you run that, so you update when you choose to and if something breaks you git checkout flake.lock and you're back. If you want it automated there's a GitHub action (DeterminateSystems/update-flake-lock) that opens a PR with the bumped lock on a schedule, and Renovate handles flake.lock too.

Add direnv + nix-direnv and the shell loads on cd into the project, so you never type nix develop again. That's the point where it stops feeling like extra work.

For personal tools like nvim and ripgrep that you want everywhere and not per project: quick way is nix profile install nixpkgs#neovim nixpkgs#ripgrep and later nix profile upgrade --all. The idiomatic way is home-manager, where your tool list and dotfiles live in one flake in a git repo and a new machine is a clone plus one command. I'd start with the profile commands and a devShell in one project, and only look at home-manager once you're sure you like it.

One caveat: for Rust, if you need an exact toolchain version or nightly, nixpkgs only carries current stable, so people use fenix or rust-overlay as a second flake input to declare specific builds in the closure. Most other languages just have versioned attributes like pkgs.python312 or pkgs.jdk21.

path: 0 25907795 25907968 25908097, hotness: undefined, score: 3, children: 0
borosilicate 3 points a month ago
path: 0 25863495 25864787 25865887 25866558, hotness: undefined, score: 3, children: 2
borosilicate 2 points 10 days ago

“I dipped the tip of my penis into an open sewer and got infected with HIV.”

Git hooks are a security joke.

path: 0 26143499, hotness: undefined, score: 2, children: 0
borosilicate 2 points a month ago
path: 0 25863495 25864787 25865887 25866558 25868679 25870728, hotness: undefined, score: 2, children: 0
borosilicate 2 points a month ago
path: 0 25777868 25871252, hotness: undefined, score: 2, children: 0
borosilicate 2 points a month ago
path: 0 25868395 25868603 25868677, hotness: undefined, score: 2, children: 0

thanks for using Leebra!

go to feed...