├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin └── nixify └── share └── nixify └── templates ├── generic ├── autoenv_enter ├── autoenv_leave └── shell.nix └── golang ├── NIXIFY_BUILD_INPUTS └── NIXIFY_SHELL_HOOK /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kampka/nixify/c1fd2f49eaf4424bc2a50fadbdc058c9705c594a/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Christian Kampka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install nixify all 2 | 3 | DEST_DIR ?= 4 | PREFIX ?= /usr/local 5 | 6 | all: 7 | @echo "Nothing to be done for target 'all'. To install, use 'make install'" 8 | 9 | nixify: 10 | install -D -m755 bin/nixify $(DEST_DIR)$(PREFIX)/bin/nixify 11 | install -d $(DEST_DIR)$(PREFIX)/share/ 12 | cp -r share/nixify $(DEST_DIR)$(PREFIX)/share 13 | chmod -R 755 $(DEST_DIR)$(PREFIX)/share/nixify 14 | 15 | install: nixify 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nixify 2 | 3 | 4 | Helps bootstrapping an opinionated [nix-shell](https://nixos.org/nix/manual/#sec-nix-shell) environment by creating a minimal shell.nix file and setting up an auto-env configuration for it. 5 | 6 | #### Installation 7 | 8 | `nixify` can be installed via the [Nix User Repository](https://github.com/nix-community/NUR) 9 | ```console 10 | $ nix-env -iA nur.repos.kampka.nixify 11 | ``` 12 | or 13 | ```console 14 | $ nix-shell -p nur.repos.kampka.nixify 15 | ``` 16 | or 17 | ```nix 18 | { 19 | # /etc/nixos/configuration.nix 20 | environment.systemPackages = [ 21 | kampka.nixify 22 | ]; 23 | } 24 | ``` 25 | 26 | #### Usage: 27 | ```shell 28 | $ echo $PATH 29 | /run/wrappers/bin:/etc/profiles/per-user/user/bin:/home/user/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin 30 | $ mkdir my-project 31 | $ nixify -p my-project my-project 32 | Add nix expressions to your nix shell... 33 | $ cd my-project 34 | these derivations will be built: 35 | /nix/store/dljgnw4ii05dlb5i0xfpz30acl3j52kx-foobar.drv 36 | these paths will be fetched (102.76 MiB download, 335.19 MiB unpacked): 37 | /nix/store/04c6780m5fgbfcwbnjl3qcmqzmdsn98m-rustc-1.24.0 38 | copying path '/nix/store/04c6780m5fgbfcwbnjl3qcmqzmdsn98m-rustc-1.24.0' from 'https://cache.nixos.org'... 39 | building '/nix/store/dljgnw4ii05dlb5i0xfpz30acl3j52kx-foobar.drv'... 40 | created 2 symlinks in user environment 41 | $ echo "$PATH" 42 | /nix/store/y5rlyv6nz8134s687d95ysc2gakwx7am-patchelf-0.9/bin:/nix/store/rf7pnq8qk9bkjpl4s2pm5dm2pk4yqhrc-paxctl-0.9/bin:/nix/store/gqg2vrcq7krqi9rrl6pphvsg81sb8pjw-gcc-wrapper-7.3.0/bin:/nix/store/bm7pb1s7rx1ad80706b5xqrznq7fgpgx-gcc-7.3.0/bin:/nix/store/6ca5dl2wy0nh37li1n4b152fcazsp3f6-glibc-2.26-131-bin/bin:/nix/store/cb3slv3szhp46xkrczqw7mscy5mnk64l-coreutils-8.29/bin:/nix/store/cmxaqb5cbzy4jk26na842n6hy1s4yn19-binutils-wrapper-2.28.1/bin:/nix/store/b0zlxla7dmy1iwc3g459rjznx59797xy-binutils-2.28.1/bin:/nix/store/6ca5dl2wy0nh37li1n4b152fcazsp3f6-glibc-2.26-131-bin/bin:/nix/store/cb3slv3szhp46xkrczqw7mscy5mnk64l-coreutils-8.29/bin:/nix/store/04c6780m5fgbfcwbnjl3qcmqzmdsn98m-rustc-1.24.0/bin:/nix/store/cb3slv3szhp46xkrczqw7mscy5mnk64l-coreutils-8.29/bin:/nix/store/364b5gkvgrm87bh1scxm5h8shp975n0r-findutils-4.6.0/bin:/nix/store/cg3yhpr5hfr00y0aah23pgxiijpzl6nz-diffutils-3.6/bin:/nix/store/navldm477k3ar6cy0zlw9rk43i459g69-gnused-4.4/bin:/nix/store/s63b2myh6rxfl4aqwi9yxd6rq66djk33-gnugrep-3.1/bin:/nix/store/j79xs2j519bmvq0gihz8ff4nw5aj3vlh-gawk-4.2.0/bin:/nix/store/2p00w952ljb1172nsf3jm7nsjgsw5qpw-gnutar-1.30/bin:/nix/store/d7895h1an2pgcd6a3vx3yb5fbg786lci-gzip-1.9/bin:/nix/store/qqv6r9vkpb44rw5d8wyfpx64rgqzlvdm-bzip2-1.0.6.0.1-bin/bin:/nix/store/lhp5rw0dagi5mgqwr9i3x41240ba4ypz-gnumake-4.2.1/bin:/nix/store/zqh3l3lyw32q1ayb15bnvg9f24j5v2p0-bash-4.4-p12/bin:/nix/store/gd1mp76qr4zpbw3lccivhvi30b025x51-patch-2.7.6/bin:/nix/store/aakgkcvw6j54zg38zrn1w00sgxx0zj8b-xz-5.2.3-bin/bin:/home/user/bin:/run/wrappers/bin:/etc/profiles/per-user/user/bin:/home/user/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin 43 | 44 | $ hack hack hack ... 45 | $ cd $HOME 46 | $ echo $PATH 47 | /run/wrappers/bin:/etc/profiles/per-user/user/bin:/home/user/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin 48 | ``` 49 | 50 | For details about developing with nix-shell, have a look at the 10th Nix pill: [Developing with nix-shell](https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html#developing-with-nix-shell) 51 | -------------------------------------------------------------------------------- /bin/nixify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -u 5 | set -o pipefail 6 | 7 | NIXIFY_DEBUG="${NIXIFY_DEBUG:-flase}" 8 | [ "$NIXIFY_DEBUG" = "true" ] && set -x 9 | 10 | NIXIFY_ENV_NAME="${NIXIFY_ENV_NAME:-}" 11 | SKIP_EDITOR="${SKIP_EDITOR:-false}" 12 | OVERRIDE_FILES="${OVERRIDE_FILES:-false}" 13 | 14 | NIXIFY_DIFF_TOOL="${NIXIFY_DIFF_TOOL:-vimdiff}" 15 | 16 | NIXIFY_CONFIGURE_AUTOEXEC="${NIXIFY_CONFIGURE_AUTOEXEC:-true}" 17 | NIXIFY_CONFIGURE_AUTOENV="${NIXIFY_CONFIGURE_AUTOENV:-true}" 18 | NIXIFY_AUTOENV_ENTER="${NIXIFY_AUTOENV_ENTER:-.env}" 19 | NIXIFY_AUTOENV_LEAVE="${NIXIFY_AUTOENV_LEAVE:-.out}" 20 | 21 | 22 | TARGET_DIR="${TARGET_DIR:-$(pwd)}" 23 | CURRENT_DIR="$(pwd)" 24 | WORKING_DIR="$(mktemp -d)" 25 | trap "rm -rf $WORKING_DIR" EXIT TERM 26 | 27 | 28 | NIXIFY_TEMPLATE_ROOT="$(realpath $(dirname $0)/..)/share/nixify/templates/" 29 | NIXIFY_TEMPLATE_DIR="${NIXIFY_TEMPLATE_DIR:-${HOME}/.nixify/templates}" 30 | NIXIFY_TEMPLATE="${NIXIFY_TEMPLATE:-generic}" 31 | 32 | declare -A TEMPLATES 33 | if [ -d "$NIXIFY_TEMPLATE_ROOT" ]; then 34 | while read template; do 35 | [ -d "$template" ] && TEMPLATES["$(basename $template)"]="$(realpath $template)" 36 | done < <(find $NIXIFY_TEMPLATE_ROOT -mindepth 1 -maxdepth 1 -type d) 37 | fi 38 | if [ -d "$NIXIFY_TEMPLATE_DIR" ]; then 39 | while read template; do 40 | [ -d "$template" ] && TEMPLATES["$(basename $template)"]="$(realpath $template)" 41 | done < <(find $NIXIFY_TEMPLATE_DIR -mindepth 1 -maxdepth 1 -type d) 42 | fi 43 | 44 | usage() { 45 | echo "nixify [name]" 46 | echo "Bootstrap a nix-shell environment for development" 47 | echo "" 48 | echo "Options:" 49 | echo " -p --path : Path of the directory to nixify (default: pwd)" 50 | echo " -y --yes : Always assume 'yes' when asking questions (WARNING: will override existing files)" 51 | echo " -t --template : The template to use for bootstrapping (default: generic)" 52 | echo " --no-editor : Do not launch the EDITOR for shell.nix after completion" 53 | echo " --no-autoenv : Do not configure any autoenv environemt" 54 | echo " --no-autoexec : Do not exec into nixified shell after completing the setup" 55 | echo " -h --help : Print this message and exit" 56 | echo "" 57 | echo "Templates:" 58 | for template in "${!TEMPLATES[@]}"; do echo " $template"; done 59 | echo "" 60 | } 61 | 62 | error_exit() { 63 | echo "[ERROR] $@" 1>&2 64 | exit 1 65 | } 66 | 67 | ask_file_override() { 68 | local filename="${1:-}" 69 | local source_file="${2:-}" 70 | 71 | ([ -e "${filename}" ] && [ "${OVERRIDE_FILES}" = "false" ]) || return 0 72 | ([ -e "${filename}" ] && [ "$(sha1sum ${source_file} | cut -d' ' -f 1)" = "$(sha1sum ${filename} | cut -d' ' -f 1)" ] ) && return 0 73 | 74 | read -p "File ${filename} already exists. Override? ([y]es / [d]iff / [N]o) " -n 1 -r 75 | echo # (optional) move to a new line 76 | [[ $REPLY =~ ^[Yy]$ ]] && return 0 77 | [[ $REPLY =~ ^[Dd]$ ]] && { 78 | $NIXIFY_DIFF_TOOL "$source_file" "$filename" || error_exit "Failed to run diff tool '$NIXIFY_DIFF_TOOL' on $(basename $filename)" 79 | return 0 80 | } 81 | return 1 82 | } 83 | 84 | build_temp_files() { 85 | local template_dir="${TEMPLATES[$NIXIFY_TEMPLATE]}" 86 | local generic_dir="${TEMPLATES[generic]}" 87 | 88 | while read t; do 89 | cp -a "$t" "$WORKING_DIR" 90 | done < <(find $generic_dir -mindepth 1 -maxdepth 1 ! -regex ".*NIXIFY_[A-Z_]+$" ! -iregex ".*README[^/]*$" ! -iregex ".*LICENSE[^/]*$") 91 | 92 | if [ "$template_dir" != "$generic_dir" ]; then 93 | while read t; do 94 | cp -a "$t" "$WORKING_DIR" 95 | done < <(find $template_dir -mindepth 1 -maxdepth 1 ! -regex ".*NIXIFY_[A-Z_]+$" ! -iregex ".*README[^/]*$" ! -iregex ".*LICENSE[^/]*$") 96 | fi 97 | 98 | find $WORKING_DIR -name "autoenv_enter" -exec mv {} "$(dirname {})/${NIXIFY_AUTOENV_ENTER}" \; 99 | find $WORKING_DIR -name "autoenv_leave" -exec mv {} "$(dirname {})/${NIXIFY_AUTOENV_LEAVE}" \; 100 | } 101 | 102 | replace_in_working_dir() { 103 | local replacement_name="$1" 104 | local replacement_value="$2" 105 | local file="$3" 106 | 107 | awk -v r="${replacement_value}" "{gsub(/ *{{${replacement_name}}}/,r)}1" "$file" > "$file.new" 108 | mv "$file.new" "$file" 109 | } 110 | 111 | populate_templates() { 112 | local template_dir="${TEMPLATES[$NIXIFY_TEMPLATE]}" 113 | 114 | while read file; do 115 | replace_in_working_dir NIXIFY_ENV_NAME "$NIXIFY_ENV_NAME" "$file" 116 | 117 | for varname in $(cat "$file" | grep -o -E "\{\{[A-Z_]+}}" | grep -o -E "[A-Z_]+" ); do 118 | if [ -e "$template_dir/$varname" ]; then 119 | # Esape amperand literals in file content to satisfy awk's escaping requirements 120 | # See https://www.gnu.org/software/gawk/manual/html_node/Gory-Details.html for details 121 | local var_value="$(cat "$template_dir/$varname" | awk '{gsub("&", "\\\\\\\\&")}1' )" 122 | replace_in_working_dir "$varname" "$var_value" "$file" 123 | else 124 | replace_in_working_dir "$varname" "" "$file" 125 | fi 126 | done 127 | done < <(find $WORKING_DIR -type f ) 128 | } 129 | 130 | populate_target_dir() { 131 | for target in $(find "${WORKING_DIR}" -mindepth 1 -maxdepth 1); do 132 | local relative_path="$(echo $target | sed 's#'"${WORKING_DIR}"'##g')" 133 | if ask_file_override "${TARGET_DIR}/$relative_path" "$target"; then 134 | cp -a "$target" "${TARGET_DIR}/" 135 | fi 136 | done 137 | } 138 | 139 | generate_gitignore() { 140 | if [ -d "${TARGET_DIR}/.git" ]; then 141 | echo "" >> "${TARGET_DIR}/.gitignore" 142 | 143 | grep -q "shell.nix" "${TARGET_DIR}/.gitignore" || echo "shell.nix" >> "${TARGET_DIR}/.gitignore" 144 | grep -q ".env" "${TARGET_DIR}/.gitignore" || echo ".env" >> "${TARGET_DIR}/.gitignore" 145 | grep -q ".out" "${TARGET_DIR}/.gitignore" || echo ".out" >> "${TARGET_DIR}/.gitignore" 146 | fi 147 | } 148 | 149 | while [ "$#" -gt 0 ]; do 150 | case $1 in 151 | -h|--help) 152 | usage 153 | exit 0 154 | ;; 155 | --no-editor) 156 | SKIP_EDITOR="true" 157 | ;; 158 | --no-autoenv) 159 | NIXIFY_CONFIGURE_AUTOENV="false" 160 | ;; 161 | --no-autoexec) 162 | NIXIFY_CONFIGURE_AUTOEXEC="false" 163 | ;; 164 | --diff-tool) 165 | shift 166 | NIXIFY_DIFF_TOOL="$(command -v $1)" || error_exit "Diff tool not found or not executable: $1" 167 | [ -x "$NIXIFY_DIFF_TOOL" ] || error_exit "Diff tool not found or not executable: $1" 168 | ;; 169 | -y|--yes) 170 | OVERRIDE_FILES="true" 171 | ;; 172 | -p|--path) 173 | shift 174 | TARGET_DIR="$(realpath $1)" 175 | [ -d "$TARGET_DIR" ] || error_exit "Path $1 is not a directory" 176 | ;; 177 | -t|--template) 178 | shift 179 | NIXIFY_TEMPLATE="$1" 180 | if [ ! "${TEMPLATES[$NIXIFY_TEMPLATE]+_}" ]; then 181 | usage 182 | error_exit "Unknown template: $NIXIFY_TEMPLATE" 183 | fi 184 | ;; 185 | *) 186 | if [ -z "${NIXIFY_ENV_NAME}" ]; then 187 | NIXIFY_ENV_NAME="$1" 188 | else 189 | echo "Unknown option: $1" 1>&2 190 | usage 191 | exit 1 192 | fi 193 | ;; 194 | esac 195 | shift 196 | done 197 | 198 | [ -n "$NIXIFY_ENV_NAME" ] || { 199 | usage 200 | error_exit "No name given" 201 | } 202 | 203 | NIXIFY_TEMPLATE_PATH="${TEMPLATES[$NIXIFY_TEMPLATE]}" 204 | [ -d "$NIXIFY_TEMPLATE_PATH" ] || error_exit "Template $NIXIFY_TEMPLATE does not exist." 205 | 206 | cd "$WORKING_DIR" 207 | 208 | build_temp_files 209 | populate_templates 210 | populate_target_dir 211 | generate_gitignore 212 | 213 | ([ -n "$EDITOR" ] && [ -e "${TARGET_DIR}/shell.nix" ] && [ "${SKIP_EDITOR}" != "true" ]) && "$EDITOR" +8 "${TARGET_DIR}/shell.nix" 214 | 215 | if [ "$NIXIFY_CONFIGURE_AUTOEXEC" != "false" ]; then 216 | SHELL=${SHELL:-"$(command -v bash)"} 217 | if [ -n "$SHELL" ]; then 218 | exec $SHELL -c "cd ${TARGET_DIR} && exec $SHELL" 219 | fi 220 | fi 221 | -------------------------------------------------------------------------------- /share/nixify/templates/generic/autoenv_enter: -------------------------------------------------------------------------------- 1 | if [ -z "${NIX_SHELL_EXEC}" ]; then 2 | [ -n "$SHELL" ] || SHELL="$(command -v bash)" 3 | export NIX_SHELL_EXEC=1 4 | 5 | export NIX_SHELL_OLDPATH="$PATH" 6 | 7 | # Support finding the shell.nix file when changing into a deeper directory 8 | CURRENT_DIR="$(pwd)" 9 | while ([ ! -e "$CURRENT_DIR/shell.nix" ] && [ ! -e "$CURRENT_DIR/default.nix" ]); do 10 | if [ "$CURRENT_DIR" = "/" ] || [ "$CURRENT_DIR" = "$(dirname $0)" ]; then 11 | echo "No shell.nix or default.nix found. Skipping nix shell invocation..." 1>&2 12 | exec $SHELL 13 | fi 14 | CURRENT_DIR="$(realpath $CURRENT_DIR/..)" 15 | done 16 | [ -e "$CURRENT_DIR/default.nix" ] || CURRENT_DIR="$CURRENT_DIR/shell.nix" 17 | 18 | [ -e "$0.include" ] && . "$0.include" 19 | 20 | exec nix-shell --run "export SHELL=$SHELL ; exec $SHELL; return" "$CURRENT_DIR" 21 | fi 22 | -------------------------------------------------------------------------------- /share/nixify/templates/generic/autoenv_leave: -------------------------------------------------------------------------------- 1 | 2 | [ -n "$NIX_SHELL_OLDPATH" ] && export PATH="$NIX_SHELL_OLDPATH" 3 | 4 | unset NIX_SHELL_OLDPATH 5 | unset NIX_SHELL_ENV 6 | 7 | if [ -n "$NIX_SHELL_EXEC" ]; then 8 | unset NIX_SHELL_EXEC 9 | [ -n "$SHELL" ] || SHELL="$(command -v bash)" 10 | exec $SHELL 11 | fi 12 | -------------------------------------------------------------------------------- /share/nixify/templates/generic/shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | nixpkgs = import { 3 | config.allowUnfree = false; 4 | overlays = [ ]; 5 | }; 6 | in 7 | with nixpkgs; 8 | stdenv.mkDerivation rec { 9 | name = "{{NIXIFY_ENV_NAME}}"; 10 | env = buildEnv { name = name; paths = buildInputs; }; 11 | buildInputs = [ 12 | # List packages that should be on the path 13 | # You can search for package names using nix-env -qaP | grep 14 | {{NIXIFY_BUILD_INPUTS}} 15 | ]; 16 | 17 | shellHook = '' 18 | export NIX_SHELL_ENV=${name} 19 | 20 | {{NIXIFY_SHELL_HOOK}} 21 | ''; 22 | } 23 | -------------------------------------------------------------------------------- /share/nixify/templates/golang/NIXIFY_BUILD_INPUTS: -------------------------------------------------------------------------------- 1 | go 2 | gnumake 3 | -------------------------------------------------------------------------------- /share/nixify/templates/golang/NIXIFY_SHELL_HOOK: -------------------------------------------------------------------------------- 1 | # Set root of the gopath to where the shell.nix is located 2 | GOPATH="$(pwd)" 3 | while [ ! -e "$GOPATH/shell.nix" ] && [ "$GOPATH" != "/" ]; do 4 | GOPATH="$(realpath $GOPATH/..)" 5 | done 6 | [ -e "$GOPATH/shell.nix" ] || { 7 | echo "No shell.nix found in expected GOPATH. This is unexpected. GOPATH not set." 1>&2 8 | exit 1 9 | } 10 | export GOPATH="$GOPATH" 11 | 12 | mkdir -p $GOPATH/{bin,pkg,src} 13 | if [ ! -e "$GOPATH/.go-autopath" ]; then 14 | touch "$GOPATH/.go-autopath" 15 | echo "# Config file to configure whether or not to register GOPATH/bin in PATH automatically" >> "$GOPATH/.go-autopath" 16 | echo "# Generated by nixify" >> "$GOPATH/.go-autopath" 17 | [ -e "$GOPATH/.gitignore" ] && echo ".go-autopath" >> "$GOPATH/.gitignore" 18 | 19 | read -p "Automatically add GOPATH/bin to you PATH? (y/n)" -n 1 -r 20 | echo 21 | if [[ $REPLY =~ ^[Yy]$ ]] ; then 22 | echo "NIXIFY_GO_AUTOPATH=true" >> "$GOPATH/.go-autopath" 23 | else 24 | echo "NIXIFY_GO_AUTOPATH=false" >> "$GOPATH/.go-autopath" 25 | fi 26 | fi 27 | 28 | if [ -e "$GOPATH/.go-autopath" ]; then 29 | NIXIFY_GO_AUTOPATH=false 30 | . "$GOPATH/.go-autopath" 31 | if [ "$NIXIFY_GO_AUTOPATH" = "true" ]; then 32 | export PATH="$GOPATH/bin:$PATH" 33 | fi 34 | fi 35 | --------------------------------------------------------------------------------