Drop #426 (2024-02-23): Weekend Project Edition

Give Pixi A Chance

(Apologies for no Thursday Drop, again. I got stuck in Kubernetes limbo, which — oddly enough — is a redundant term.)

Ev’rybody’s talking about uvvenvpoetry, and renvNixs and condas, and Cargos, and spacks. And, aye yai yai (with apologies to Mr. Lennon, RIP).

These days, things move way too fast, and that pace of change is a huge detriment to software development, reproducible data analyses, data engineering pipelines, backward compatibility, and, more often than not, just getting stuff done. In other words, “dependency hades” — along with configuration management — is quite a real phenomenon.

The non-exhaustive list of both ecosystem specific, and “generic” package/environment managers in the highly lyrically suspect opening sentence all attempt to help with this situation, and so does Pixi, the subject of this week’s WPE.

What Is Pixi?

Pixi is a Rust-based, cross-platform, multi-language package manager and workflow tool that’s built on the, er…solid? foundation of the Conda ecosystem. It supports multiple languages, including Python, C++, and R, using Conda packages (it also supports many other programming languages). This means you can manage packages for multiple ecosystems with the same tool, on a per-project basis. It works across Linux, Windows, and macOS, so your team can all take advantage of the same moving parts. And, it can work at both the project and system level.

Getting Started

They ask you to do the curl|bash dance (the script was safe as of the time of the Drop) to get set up, but there are Homebrew, native installer, and direct Cargo installation options as well. To see if it got added properly, you can do:

$ pixi info
      Pixi version: 0.14.0
          Platform: osx-arm64
  Virtual packages: __unix=0=0
                  : __osx=14.4=0
                  : __archspec=1=arm64
         Cache dir: /Users/hrbrmstr/Caches/rattler/cache
      Auth storage: /Users/hrbrmstr/.rattler/credentials.json

That “rattler” path refers to the Rust library and set of tools for the conda ecosystem.

I recommend running pixi --help (or tap here) just to see what’s available, and also recommend following their instructions for installing shell completions.

Just to get familiar with how it works, go ahead and do:

$ pixi init sup
✔ Initialized project in /Users/hrbrmstr/projects/p/sup

That creates the sup directory and adds a pixi.lock file, which is used to record the exact versions of dependencies that have been resolved and installed in a project. It also adds a pixi.toml:

[project]
name = "sup"
version = "0.1.0"
description = "Add a short description here"
authors = ["hrbrmstr <bob@rud.is>"]
channels = ["conda-forge"]
platforms = ["osx-arm64"]

[tasks]

[dependencies]

We use pixi add to add tools/dependencies to the project. These can be versioned with the familiar =##.## postfix (e.g., python=3.9).

$ pixi add R
✔ Added R

That fetched R for this project and updated our dependencies:

[dependencies]
R = ">=4.3,<5"

(You will want to run through the online docs for the full toml config spec for pixi).

It also created a .pixi directory:

$ tree .pixi -L 3
.pixi
└── envs
    └── default
        ├── bin
        ├── cmake
        ├── conda-meta
        ├── etc
        ├── fonts
        ├── include
        ├── lib
        ├── libexec
        ├── man
        ├── prefix
        ├── sbin
        ├── share
        ├── ssl
        └── var

There’s quite a bit of bits in there:

$ du -sh sup
738M    sup

so, make sure you’ve got plenty of storage space.

We can make sure it did what it said:

$ pixi run R --version --quiet
R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin20.0.0 (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.

Pixi activated the local environment, then ran R in that context. You can do pixi shell to run things in the content of the environment without the need to prefix the commands with pixi run.

T[a]sk, t[a]sk

Most projects have steps that need to be run. Pixi calls these “tasks“, and they can be standalone items or depend on other tasks. Let’s add a task that installs some R packages.

[tasks]
# install necessary R packages
setup_r = "Rscript -e 'install.packages(c(\"jsonlite\", \"httr\"))'"

If you run pixi run setup_r, you’ll see it perform said operation. NOTE: For now, you should consider using one of R’s environment managers on top of pixi if you need to pin items to a specific version.

Before adding more tasks, let’s add some more pixi-level dependencies:

$ pixi add nodejs=20 r-recommended curl
✔ Added nodejs=20
✔ Added r-recommended
✔ Added curl

We’ll use this setup to make a reproducible Observable Framework dashboard. The code is on Codeberg, and there you’ll see we’ve added:

.
├── docs
│   ├── data
│   │   └── mtcars.json.R
│   └── index.md
├── observablehq.config.ts
└── package.json

into the mix. The R file will be run by Observable’s Framework on dev/build and use our pixi’d R to generate the mtcars.json file. We’ll also add some helper tasks to the config:

[project]
name = "sup"
version = "0.1.0"
description = "A reproducible Obserable Framework project"
authors = ["hrbrmstr <bob@rud.is>"]
channels = ["conda-forge"]
platforms = ["osx-arm64"]

[tasks]
# install necessary R packages
setup_r = "Rscript -e 'install.packages(c(\"jsonlite\", \"httr\"))'"

# setup node project
setup_node = "npm install"

# run in dev mode
dev = "npm run dev"

# run in dev mode
build = "npm run build"

[dependencies]
R = ">=4.3,<5"
r-recommended = ">=4.3,<5"
nodejs = "20.*"
curl = ">=8.5.0,<8.6"

If you execute pixi run dev, it will activate the local environment and fire up the server and browser. Similarly, pixi run build will build the “dashboard”.

Your Turn

If you want to build on this foundation, consider adding Python into the mix and using it plus curl to add more data loaders to the Framework project. Also, see what happens when you make the dev or build tasks dependent on other tasks. While you’re at it, make the index.md far less lame.

Otherwise, try converting one of your existing projects to a pixi-managed one.

The config also supports designating a README, so consider making a good one for either path you take.

Remember, this example project is on Codeberg.

FIN

Remember, you can follow and interact with the full text of The Daily Drop’s free posts on Mastodon via @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev ☮️

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.