5

I have the following shell.nix (to setup my development environment, no NixOS):

with import <nixpkgs> {};

stdenv.mkDerivation {
    name = "my-shiny-project";
    buildInputs = [
        jq
        nodejs-6_x
        #postgis {version="2.3.1";}
        #postgis ("2.3.1")
        #postgis "2.3.1"
        postgresql96
        zsh
    ];
    shellHook = ''
        export SHELL=zsh
        export PATH="$PWD/node_modules/.bin/:$PATH"
    '';
}

PostGIS expects a version parameter. I'm not sure how to pass that parameter along. Whether I use postgis{version="2.3.1";}, postgis("2.3.1") or postgis "2.3.1", I receive the following error:

error: cannot coerce a set to a string, at /nix/store/0rj9y7gvzzahp93cvdmrwc2v2aznh61p-nixpkgs-18.03pre118061.69607d7662/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:98:11

In the Nameless and single parameter section of functions and imports Nix pill, I see the syntax for calling a function is simply NAME PARAM.

What do I have to add to buildInputs to get PostGIS installed for that specific version of PostgreSQL?

François Beausoleil
  • 16,265
  • 11
  • 67
  • 90
  • Where do you see that PostGIS expects a version number as a parameter? The package is defined as a function which expects a set, and I don't see `version` as an expected attribute: https://github.com/NixOS/nixpkgs/blob/4ea954477f8ed8995119699bb7472b3295a77c02/pkgs/development/libraries/postgis/default.nix – Emmanuel Rosa Oct 18 '17 at 14:05
  • @EmmanuelRosa, in one of the attempts I made, I was told that postgis expected a version. Sadly, I can't get back at what I did then. – François Beausoleil Oct 18 '17 at 14:16

2 Answers2

6

Recently it became possible to use postgresql.withPackages:

with import <nixpkgs> {};

mkShell {
    buildInputs = [
        jq
        nodejs
        ( postgresql11.withPackages (p: [ p.postgis ]) )
        zsh
    ];
    shellHook = ''
        export SHELL=${zsh}/bin/zsh
        export PATH="${builtins.toPath ./.}/node_modules/.bin/:$PATH"
        export PGDATA=${builtins.toPath ./.}/pg
        export PGHOST=$PGDATA

        pg_ctl initdb
        pg_ctl -o "-p 5555 -k $PGDATA" start
        psql -p 5555 postgres -c 'create extension postgis' || true
    '';
}
danbst
  • 3,363
  • 18
  • 38
1

This is truly a stab in the dark given that postgresql is designed to be used as a service, but it's only available to you as a package since you're not on NixOS. But try this:

with import <nixpkgs> {};

stdenv.mkDerivation {
    name = "my-shiny-project";
    buildInputs = [
        jq
        nodejs-6_x
        postgis.v_2_3_1
        postgresql96
        zsh
    ];
    shellHook = ''
        export SHELL=zsh
        export PATH="$PWD/node_modules/.bin/:$PATH"
    '';
}

Explanation

The postgis package doesn't produce a derivation, so it's output cannot be used directly. Instead it produces a set with attributes for two versions: 2.3.1 and 2.4.0.

Caveat

The above may not work at all. The issue is that postgis is normally provided to postgresql via the extraPlugins attribute like so:

services.postgresql.extraPlugins = [ (pkgs.postgis.override { postgresql = pkgs.postgresql95; }).v_2_3_1 ];

This causes postgresql to be installed in such a way that it can see the postgis library. However, this is done at the service level, not the package leve, and services are only available to NixOS. So if the above doesn't work, try this hunk of code:

with import <nixpkgs> {};
let
   pg = postgresql96;

   postgresqlWithPlugins =
     buildEnv {
      name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
      paths = [ pg pg.lib (postgis.override { postgresql = pg; }).v_2_3_1) ];
      buildInputs = [ makeWrapper ];
      postBuild =
        ''
          mkdir -p $out/bin
          rm $out/bin/{pg_config,postgres,pg_ctl}
          cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
          wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
        '';
};
in
stdenv.mkDerivation {
    name = "my-shiny-project";
    buildInputs = [
        jq
        nodejs-6_x
        postgresqlWithPlugins
        zsh
    ];
    shellHook = ''
        export SHELL=zsh
        export PATH="$PWD/node_modules/.bin/:$PATH"
    '';
}

Basically, this is an untested port of the extraPlugins implementation.

Emmanuel Rosa
  • 9,697
  • 2
  • 14
  • 20
  • Sadly, neither of the techniques work. In both cases, Nix tells me: `error: attribute ‘version’ missing, at /nix/store/0rj9y7gvzzahp93cvdmrwc2v2aznh61p-nixpkgs-18.03pre118061.69607d7662/nixpkgs/pkgs/development/libraries/postgis/default.nix:36:17`. – François Beausoleil Oct 19 '17 at 12:55
  • Are you saying is that I can't run both PG95 and PG96 on the same machine at the same time, because the PG derivation is designed to be used as a service? My ultimate goal is to have a reproducible development environment for me and my colleagues, reproducing Heroku as much as possible. Can Nix help with that? I thought this *was* it's "raison d'être". – François Beausoleil Oct 19 '17 at 12:57
  • The postgresql packageprovides postgresql itself, but it does not provide a way to run it as a service, which is what you usually want. The same applies to other services. NixOS modules are used to provide services. For example, here's how the postgresql NixOS module sets up a systemd service to run postgresql: https://github.com/NixOS/nixpkgs/blob/release-17.09/nixos/modules/services/databases/postgresql.nix#L201 That same file shows a config option which defines the package; that's where the postgresql package comes in. – Emmanuel Rosa Oct 19 '17 at 17:23
  • Can you run two versions of postgresql ad the same time? Well, not by default. But you can duplicate the service to add an additional instance. Here's my video showing how to create an ad-hoc NixOS module: https://www.youtube.com/watch?v=20vDArWx3o8 If you want to duplicate Heroku, NixOps may be your best bet. In fact, it can deploy to Heroku. – Emmanuel Rosa Oct 19 '17 at 17:26
  • It’s definitely possible to run multiple instances at the same time. It is also possible to not install them globally or store their state in a global location. All you need is initdb to create the cluster, and then pass the created directory to postgres with the -D flag or the PGDATA environment variable. The “run as a service” part is a convenience provided by many distributions, but postgres itself doesn’t know. What those conveniences do under the hood, you can do manually if so desired (and this is very sensible for reproducible development environments). –  May 08 '21 at 12:06