Related to this question: nix-shell: how to specify a custom environment variable?
With this derivation:
stdenv.mkDerivation rec {
FOO = "bar";
}
FOO will be available in the nix shell as an environment variable, but is it possible to load environment variables from an env file?
You could use nix-shell's shellHook to load environment variables from a file by sourcing them as shell code. For example:
stdenv.mkDerivation {
name = "my-shell";
shellHook = ''
# Mark variables which are modified or created for export.
set -a
source env.sh
# or to make it relative to the directory of this shell.nix file
# source ${toString ./env.sh}
set +a
'';
}
You could switch from stdenv.mkDerivation to mkShell if your shell isn't also a package.
Related
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
'';
I'm trying to use nix-shell with a shell.nix file to get a clean development environment but I don't know how to change the location of the temporary build directories.
The buildInputs packages are built in /tmp but this path doesn't have enough space and I get an error: [Errno 28] No space left on device during the build.
I tried running nix-shell with a modified TMPDIR environment variable but it only affects the location of the nix-shell temporary files. The nix-build files are still put in /tmp.
I also tried to export a new value for TMPDIR in the shellHook but it doesn't work.
How can I change the TMPDIR of nix-build when it's started by nix-shell?
Here's my shell.nix:
let
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
name = "something";
buildInputs = with pkgs; [
python38
python38Packages.pytorchWithCuda
];
shellHook = ''
'';
}
I got the answer on the NixOS forum:
If this is a mutli-user install, you need to modify the Nix daemon’s TMPDIR.
To do that on my system I created a /etc/systemd/system/nix-daemon.service.d/override.conf with:
[Service]
Environment=TMPDIR=/var/tmp/nix-daemon
When I am trying to set environment variables using PowerShell in Windows Terminal with the command set test1=value1, I get no errors. However, when I try to check all environment variables using the set command, I get the following prompt:
cmdlet Set-Variable at command pipeline position 1
Supply values for the following parameters:
Name[0]:
I read that when using PowerShell you set environment vars using this:
$Env:test1 = "value1";
I want to set the variables so that on my backend in custom-environment-variables.json
I can store a name by which config can extract it using config.get("test").
custom-environment-variables.json:
{
"test": "test1",
}
But every time I try this, it says Error: Configuration property "test" is not defined.
Doing the same procedure CMD (either directly or through Windows Terminal) I get no issues whatsoever. Any ideas what might be causing this?
First, the easy part:
I get no errors but when I try to check all env. variables calling "set" I get the following prompt:
That's because the set command in PowerShell behaves differently. It's an alias for the PowerShell Set-Variable cmdlet. You can see this with Get-Alias.
Also, PowerShell variables are not environment variables. As you commented, the proper way to set an environment variable in PowerShell is with:
$env:variablename = "value"
The equivalent command to set (to get a list of all environment variables and their values) in PowerShell is:
Get-ChildItem env:
# Or using the alias
dir env:
# Or using another alias
ls env:
This access the PowerShell "environment provider", which is essentially (my grossly oversimplified summary) a "virtual drive/filesystem" that PowerShell provides which contains the environment variables. You can also create variables in here.
More reading: about_Environment_Variables from the PowerShell Doc.
As for the core issue with the config module, I haven't been able to reproduce that. It works correctly for me in both PowerShell and CMD. So let me run through my results in the hopes that it will help you see what might be different. All tests were performed in Windows Terminal, although as we've determined in the comments, this is more a difference in PowerShell vs. CMD for you:
config\default.json:
{
"test": "Original Value"
}
config\custom-environment-variables.json:
{
"test": "test1"
}
CMD without test1 variable set:
Running node in CMD:
> const config = require('config')
undefined
> config.get('test')
'Original Value'
>
CMD with test1 variable set:
Exit Node, and back in CMD:
>set test1=Override
>node
In Node:
Welcome to Node.js v14.16.1.
Type ".help" for more information.
> const config = require('config')
undefined
> config.get('test')
'Override'
>
PowerShell without test1 variable set:
Welcome to Node.js v14.16.1.
Type ".help" for more information.
> const config = require('config')
undefined
> config.get('test')
'Original Value'
>
PowerShell with test1 variable set:
In PowerShell:
PS1> $env:test1="Override"
PS1> node
In Node:
Welcome to Node.js v14.16.1.
Type ".help" for more information.
> const config = require('config')
undefined
> config.get('test')
'Override'
>
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).
I have a list of file names with environment variables in them. I'd like to read the file line by line and then set a variable to the read in line however have the envirnment variable translated to the appropriate environment variable. Here is my script so far:
#!/bin/ksh
. /test/currentEnv.sh
while read line
do
echo $line
done < $1
if my source file is:
foo1$ENVVAR1.csv
foo2$ENVVAR2.csv
foo3$ENVVAR3.csv
and my Environment variables in currentEnv.sh are
$ENVVAR1=hello; export ENVVAR1
$ENVVAR2=world; export ENVVAR2
$ENVVAR3=test; export ENVVAR3
I'd like the results of the script to be
foo1hello.csv
foo2world.csv
foo3test.csv
currently it just dumps out the original file:
foo1$ENVVAR1.csv
foo2$ENVVAR2.csv
foo3$ENVVAR3.csv
Edit
I was able to get the majority of my files resolved using:
#!/bin/ksh
. /test/currentEnv.sh
while read line
do
eval echo $line
done < $1
however some of my variables are in the middle of string like:
foo3$ENVVAR3_bar.csv
this seems to look for an env variable $ENVVAR3_bar and doesn't find it I need this to output:
foo3test_bar.csv
You declare a variable without the dollar sign:
$var=value # no
var=value # yes
Since underscore is a valid character for a variable name, ksh is trying to expand the variable named ENVVAR3_bar: you need to use braces to separate the variable name from the surrounding text:
foo3$ENVVAR3_bar.csv # no
foo3${ENVVAR3}_bar.csv # yes