How to package pgenv, a program that compiles postgres?

Hi!

I’m trying to package pgenv. From its own README:

pgenv is a simple utility to build and run different releases of PostgreSQL. This makes it easy to switch between versions when testing applications for compatibility.

Basically, pgenv is able to list every existing version of postgresql, download one, compile it, create a database and configure psql to to point to it.

As its core, it’s just a shell script, so my first package derivation is defined as such:

  pgenv = {stdenv, lib, fetchurl, bash, writeShellScript,
      # postgres deps build will probably go there
#      glibc, zlib, readline, openssl, icu, lz4, zstd, systemd, libossp_uuid
#     , pkg-config, libxml2, tzdata, libkrb5
  }:
    stdenv.mkDerivation rec {
      pname = "pgenv";
      version = "1.3.5";
      src = fetchurl {
        url = "https://github.com/theory/pgenv/archive/refs/tags/v${version}.tar.gz";
        hash = "sha256-rLGcUV5xBZ5Rgz+AsXwmoIliFhJNfS/nIsOtcQC+s+U=";
      };
      nativeBuildInputs = [ bash ];
      installPhase = ''
        mkdir -p $out
        cp -rv . $out
        mv $out/bin/pgenv $out/bin/pgenv_orig
        echo '#!${bash}/bin/bash' > $out/bin/pgenv
        echo "PGENV_ROOT=\$HOME $out/bin/pgenv_orig \$@" >> $out/bin/pgenv
        chmod a+x $out/bin/pgenv
       '';
      # builder = pkgs.writers.writeBash "pgenv" ''
      #   export PGENV_ROOT=$HOME
      #   ./bin/pgenv
      # '';
    };

With this I can execute some pgenv commands, but not the pgenv build <pgversion>.

I have 2 questions:

  • pgenv calculates PGENV_ROOT (a var that defines where new pg databases go) in a manner that is incompatible with nixos (it resolves to a store folder). So I moved pgenv to pgenv_orig and wrapped it while setting explicitly PGENV_ROOT to the path it is resolved to by default in ubuntu. Is this a good way to do this? It’s a bit manual but it works…
  • now I need to make all postgresql dependencies available inside pgenv script, so that ./configure in the downloaded postgres folder finds the build dependencies. How can I do that? My intuition is that I need something like buildFHSEnv, but it’s unclear to me where it should go. Does it need to wrap my whole derivation function?

Thanks!

Basically, I think I need a FHS env, but not at build time.

Aaand I think I just need to get inspired by Trying to contribute to nixpkgs but only buildFHSEnv works for binary - #3 by adam248

Is the tool a hard requirement? What specific affordances are you using it for?

(I’m a little skeptical of this–it looks like there’s a pretty big gap between pgenv’s Way and The Nix Way, and in some cases it can end up being tricky to square stuff like this. Large Shell projects also tend to be tricky to package well. I think it’s worth teasing out the above to figure out if there are existing Nix-ecosystem tools/approaches that do what you need.)

3 Likes

I was at a similar point in my thinking process :smile:

pgenv provides several things:

  1. a way to have multiple version of postgresql, way more than what is packaged for debian for instance
  2. easy command to manipulate (create, destroy, stop, start, get logs etc…) a postgresql cluster
  3. a default - dev-oriented - connexion config (so that by default you can just “psql -U postgres” in your cluster)

For 1., Nix Package Versions fills that need completely, in a nix-friendly way.

For 2, maybe it’s just a matter of getting use to standard postgres cli commands, or create wrapper around it.

For 3, same as 2.

So I need to weight if packaging pgenv (and maintaining this package!) is less work than recreating the wrapper.

Thanks for your input!

Assuming these databases are for development, you might want to look into some of the nix-ecosystem dev-environment tools such as devbox/devenv/devshell/flox (there might be more I’m forgetting). I can’t comment directly as I haven’t used them, but I think managing dev databases is a common theme (see, for example, the Run Services section at https://devenv.sh).

I have a blog post that outlines the main approaches to shell packaging: no-look, no-leap Shell script dependencies. That said, I wrote the example used there to show how Nix can enable us to avoid some common tropes in large Shell projects–you may have to deal directly with those if you package it.

(Real-world Shell projects tend to either be really vulnerable to environment differences or end up with a lot of defensive sanity checking. They might have multiple fallbacks if you don’t have one tool or another, or they may do very dynamic stuff like building up commands in a variables before executing them later. No approach is bulletproof enough to just fully automate.)

As to specifics on your example:

  • the idiomatic way to create the wrapper you’re making would be to use makeWrapper. (Note that I’m not saying that a wrapper is or isn’t the best approach here, just pointing you towards the pattern you’ll see for building wrappers in nixpkgs.)
  • Setting the PGENV_ROOT to $HOME smells right; makeWrapper has a flag for that as well.
  • Unless you do something with FHSEnv or nix-shell, you’ll likely need one of the approaches outlined in my post to address all of the individual commands in the script.
2 Likes

Thanks for these ideas! Currently, I’m testing devenv.sh, which seems to do the job for my dev workflow.

1 Like