How to install PostGIS within a Nix environment - nix

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?

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
'';
}

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.

Related

Can I start nix-shell with packages from different revisions?

I can start nix-shell with a package from a particular revision, e.g.
nix-shell -p ktlint -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/141439f6f11537ee349a58aaf97a5a5fc072365c.tar.gz
nix-shell -p jq -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/7d7622909a38a46415dd146ec046fdc0f3309f44.tar.gz
Can I start nix-shell with two packages, but from different revisions, in one command? For example, if I wanted both ktlint and jq from the specific revisions above?
Setting NIX_PATH=nixpkgs=... is just syntactic sugar enabling references like <nixpkgs> to work; but one doesn't need to use import <nixpkgs> exclusively -- one can also import directly from an explicit path.
nix-shell -E '
let
pkgsA = (import (builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/141439f6f11537ee349a58aaf97a5a5fc072365c.tar.gz) {});
pkgsB = (import (builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/7d7622909a38a46415dd146ec046fdc0f3309f44.tar.gz) {});
in
pkgsA.mkShell {
buildInputs = [
pkgsA.ktlint
pkgsB.jq
];
}'

How to refer to custom derivation in shell.nix?

I'm very new to Nix. I'd like to refer to project scripts in my shell.nix file, so that when I cd into my project directory I can refer to them by name, and I can keep them up-to-date whenever the sources change.
To learn how to do this, I created a very simple derivation for a shell script. Eventually I'd like to use other languages, but I'm starting simple. It looks like this:
project
nix
myScript
default.nix
builder.sh
shell.nix
# default.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation rec {
name = "myScript";
echo = pkgs.coreutils + "/bin/echo";
builder = "${pkgs.coreutils}/bin/bash";
args = [ ./builder.sh ];
}
# builder.sh
$echo "$echo Hello world" > $out
When I run nix-build myScript.nix it creates a symlinked result file that looks like this:
/nix/store/3mfkgajns47hfv0diihzi2scwl4hm2fl-coreutils-9.1/bin/echo Hello world
I tried referencing this in my shell.nix file like this:
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/bf972dc380f36a3bf83db052380e55f0eaa7dcb6.tar.gz") {} }:
let
myScript = import ./myScript {};
in
pkgs.mkShell {
buildInputs = [
myScript
];
shellHook = ''
echo Loading shell.nix
'';
}
But whenever I enter the projects directory and run the command `myScript, I get an error:
zsh: command not found: myScript
I already have direnv configured correctly, which I can confirm by adding other shell tools and checking their versions. So it's something wrong with my nix files.
I'm almost certainly doing something wrong here. I know I can simplify this with pkgs.writeShellScriptBin, but the shell script is more a minimal example of what I want to get working. Eventually I'd use more complex derivations.
What I think is wrong
I think the myScript derivation or builder is doing something wrong. It does create the expected output file (i.e. I can chmod +x and run it, and it works) but I suspect I need to tell nix how to run it? I'm not sure. And also I might be importing the derivation incorrectly.
This is a problem with your default.nix, not your shell.nix.
For mkShell to work with buildInputs as you intend, you need $out to be a directory with an $out/bin/myScript, not a file on its own. nixpkgs has a helper that will do this for you, in https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/trivial-builders.nix --
# default.nix; no builder.sh needed
{ pkgs ? import <nixpkgs> {} }:
pkgs.writeShellScript "myScript" ''
echo "Hello world" # use the bash-builtin echo, not the external coreutils one
'';

Failed when `nix-build hello.nix`

I followed the steps on http://lethalman.blogspot.com/2014/08/nix-pill-8-generic-builders.html to build GNU Hello, and here is the files I used to build GNU hello 2.9:
$ wget -c http://ftp.gnu.org/gnu/hello/hello-2.9.tar.gz
hello.nix:
$ cat hello.nix
let
pkgs = import <nixpkgs> {};
mkDerivation = import ./autotools.nix pkgs;
in mkDerivation {
name = "hello";
src = ./hello-2.9.tar.gz;
}
autotools.nix:
$ cat autotools.nix
pkgs: attrs:
with pkgs;
let defaultAttrs = {
builder = "${bash}/bin/bash";
args = [ ./builder.sh ];
baseInputs = [ gnutar gzip gnumake gcc binutils coreutils gawk gnused gnugrep ];
buildInputs = [];
system = builtins.currentSystem;
};
in
derivation (defaultAttrs // attrs)
builder.sh:
$ cat builder.sh
set -e
unset PATH
for p in $buildInputs; do
export PATH=$p/bin${PATH:+:}$PATH
done
tar -xf $src
for d in *; do
if [ -d "$d" ]; then
cd "$d"
break
fi
done
./configure --prefix=$out
make
make install
Error messages:
$ nix-build hello.nix
these derivations will be built:
/nix/store/d84l57agx3rmw00lxs8gjlw8srmx1bh9-hello.drv
building '/nix/store/d84l57agx3rmw00lxs8gjlw8srmx1bh9-hello.drv'...
/nix/store/vv3xqdggviqqbvym25jf2pwv575y9j1r-builder.sh: line 7: tar: No such file or directory
builder for '/nix/store/d84l57agx3rmw00lxs8gjlw8srmx1bh9-hello.drv' failed with exit code 127
error: build of '/nix/store/d84l57agx3rmw00lxs8gjlw8srmx1bh9-hello.drv' failed
It seems there is gnutar in the autotools.nix but builder still complains tar: No such file or directory, why is this?
The problem is probably that gnutar is in the baseInputs list, while the buildInputs list you are building your PATH from is totally empty so nothing will be on your PATH. Try changing the for line in your shell script so that it uses the concatentation of both lists to build the path:
for p in $baseInputs $buildInputs; do
You can add echo $PATH to your builder script to debug issues like this.
That is what the blog post author was asking you to do in this sentence from the post:
Complete the new builder.sh by adding $baseInputs in the for loop together with $buildInputs.

How to get the name from a nixpkgs derivation in a nix expression to be used by nix-shell?

I'm writing a .nix expression to be used primarily by nix-shell. I'm not sure how to do that. Note this is not on NixOS, but I don't think that is very relevant.
The particular example I'm looking at is that I want to get this version-dependent name that looks like:
idea-ultimate = buildIdea rec {
name = "idea-ultimate-${version}";
version = "2017.2.2"; /* updated by script */
description = "Integrated Development Environment (IDE) by Jetbrains, requires paid license";
license = stdenv.lib.licenses.unfree;
src = fetchurl {
url = "https://download.jetbrains.com/idea/ideaIU-${version}-no-jdk.tar.gz";
sha256 = "b8eb9d612800cc896eb6b6fbefbf9f49d92d2350ae1c3c4598e5e12bf93be401"; /* updated by script */
};
wmClass = "jetbrains-idea";
update-channel = "IDEA_Release";
};
My nix expression is the following:
let
pkgs = import <nixpkgs> {};
stdenv = pkgs.stdenv;
# idea_name = assert pkgs.jetbrains.idea-ultimate.name != ""; pkgs.jetbrains.idea-ultimate.name;
in rec {
scalaEnv = stdenv.mkDerivation rec {
name = "scala-env";
builder = "./scala-build.sh";
shellHook = ''
alias cls=clear
'';
CLANG_PATH = pkgs.clang + "/bin/clang";
CLANGPP_PATH = pkgs.clang + "/bin/clang++";
# A bug in the nixpkgs openjdk (#29151) makes us resort to Zulu OpenJDK for IDEA:
# IDEA_JDK = pkgs.openjdk + "/lib/openjdk";
# PATH = "${pkgs.jetbrains.idea-ultimate}/${idea_name}/bin:$PATH";
IDEA_JDK = /usr/lib/jvm/zulu-8-amd64;
# IDEA_JDK = /opt/zulu8.23.0.3-jdk8.0.144-linux_x64;
# IDEA_JDK = /usr/lib/jvm/java-8-openjdk-amd64;
buildInputs = with pkgs; [
ammonite
boehmgc
clang
emacs
jetbrains.idea-ultimate
less
libunwind
openjdk
re2
sbt
stdenv
unzip
zlib
];
};
}
I have commented out setting PATH as it depends on getting idea_name in the let-clause. As an interesting side note, as is, this does not fail if I leave it uncommented but causes a very bizarre error when executing nix-shell about not being able to execute bash. I've also tried the more simple case of let idea_name = pkgs.jetbrains.idea-ultimate.name; but this fails later on when idea_name is used in setting PATH since idea_name ends up being undefined.
Update:
I began exploring with nix-instantiate, but the derivation of interest seems empty:
[nix-shell:/nix/store]$ nix-instantiate --eval --xml -E "((import <nixpkgs> {}).callPackage ./3hk87pqgl2qdqmskxbhy23cyr24q8g6s-nixpkgs-18.03pre114739.d0d905668c/nixpkgs/pkgs/applications/editors/jetbrains { }).idea-ultimate";
<?xml version='1.0' encoding='utf-8'?>
<expr>
<derivation>
<repeated />
</derivation>
</expr>
If your intent is to get idea-ultimate into nix-shell environment, then just include that package to buildInputs. I see it's already included, so it should already be present in your PATH.
BTW, you can extend your shellHook and export PATH and other variables rather from there, where you have full bash. Why would you do it from bash? Less copying. When you specify
IDEA_JDK = /usr/lib/jvm/zulu-8-amd64;
in Nix, the file /usr/lib/jvm/zulu-8-amd64 get's copied to nix store and IDEA_JDK is set to point to file in /nix/store. Was that your intent?
Regarding nix-instantiate:
$ nix-instantiate --eval -E 'with import <nixpkgs>{}; idea.pycharm-community.outPath'
"/nix/store/71jk0spr30rm4wsihjwbb1hcwwvzqr4k-pycharm-community-2017.1"
but you still have to remove doublequotes (https://gist.github.com/danbst/a9fc068ff26e31d88de9709965daa2bd)
Also, nitpick, assert pkgs.jetbrains.idea-ultimate.name != ""; can be dropped as it's impossible to have empty derivation name in Nix.
And another nitpick. You'll soon find very incovenient to launch IDE from shell every time. It seems a good idea to specify, that some package is used for development, but nix-shell doesn't work well for non-cli applications. Not to mention occasional problems with Nix GC and nix-shell. You'd better install IDE globally or per-user, it is better long-term solution.
[ADDENDUM]
You are looking for this (dev-environment.nix):
with import <nixpkgs> { };
buildEnv {
name = "my-super-dev-env";
paths = [
#emacs
nano
idea.pycharm-community
];
buildInputs = [ makeWrapper ];
postBuild = ''
for f in $(ls -d $out/bin/*); do
wrapProgram $f \
--set IDEA_JDK "/path/to/zulu-jdk" \
--set CLANG_PATH ... \
--set CLANCPP_PATH ...
done
'';
}
which you install using nix-env -if ./dev-environment.nix. It will wrap your programs with those env vars, without polluting your workspace (you can pollute it further using nix-shell with shell hook, if you want).

using the nix typing system with `nix-instantiate`

myservice
i've written a nixos service in myservice.nix and i include it in /etc/nixos/configuration.nix with:
imports [ /path/to/myservice.nix ];
and later i'm using it inside configuration.nix:
services.myservice.enable = true;
question
in one scenario i can't use nixos-rebuild switch but because typing in nix is linked to the options system using foo = mkOption { type = types.int; ...} i'm forced to use the options systems even though i just want to compute a configuration file for nginx using nix.
how to evaluate that nginx.conf only?
#aszlig wrote me this command:
nix-instantiate --eval --strict -E '(import <nixpkgs/nixos> { configuration = { imports = [ nixcloud-reverse-proxy/nixcloud-reverse-proxy.nix ]; services.nixcloud-reverse-proxy.enable = true; }; }).config.system.build.configsFromPath'
execution results in
nix-instantiate --eval --strict -E '(import <nixpkgs/nixos> { configuration = { imports = [ ./nixcloud-reverse-proxy.nix ]; services.nixcloud-reverse-proxy.enable = true; }; }).config.system.build.configsFromPath'
error: attribute ‘configsFromPath’ missing, at (string):1:1
(use ‘--show-trace’ to show detailed location information)
update
nix-build '<nixpkgs/nixos>' -A config.systemd.services.nixcloud-reverse-proxy.runner -I nixos-config=./configuration.nix
...
/nix/store/lp2jbb1wahhlr7qkq81rmfvk84mjk1vk-nixcloud-reverse-proxy-runner
now i can use that to grep the conf file:
cat /nix/store/lp2jbb1wahhlr7qkq81rmfvk84mjk1vk-nixcloud-reverse-proxy-runner | grep -o ' /nix/store/.*nginx-reverse-proxy.conf'
... kind of a workaround but not very precise! i'd rather like a config file in a directory.
I see your file name is nginx-reverse-proxy.conf, so it isn't built with fancy NixOS module system, but with some other ways. In that case (you control how you build your file) you can include it globally:
environment.etc."myconfigs"."nginx-reverse-proxy.conf".text = ...content of that file;
Which your refer then with
$ nix-instantiate --eval -E '
with import <nixpkgs/nixos> {
configuration = { ... };
};
config.environment.etc."myconfigs"."nginx-reverse-proxy.conf".text
'
You probably need to decode that output though using trick described in https://gist.github.com/danbst/a9fc068ff26e31d88de9709965daa2bd
which is itself a convoluted way to do
$ cat $(nix-build -E '
with import <nixpkgs/nixos> {
configuration = { ... };
};
config.environment.etc."myconfigs"."nginx-reverse-proxy.conf".source
')
In case your proxy config is part of general nginx.conf script, then you still can get it using
$ cat $(cat $(nix-build -E '
with import <nixpkgs/nixos> {
configuration = ...;
};
config.system.build.units."nginx.service".unit
')/nginx.service \
| grep nginx.conf \
| sed -r 's/.* (.*nginx.conf).*/\1/g'
)
Because nginx.conf file is private to nginx module, we can't reference it directly, but have to extract it directly from usage site.
Overall, NixOS lacks a good interface to introspect its internals, but it is still possible.

Resources