Last active
October 16, 2019 16:18
-
-
Save KoviRobi/87e111b21140e73cbcb27c85708c30b9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env nix-shell | |
#! nix-shell -i bash -p nix-prefetch-git | |
# vim:ft=sh: | |
set -euo pipefail | |
if [[ "${#}" -lt 1 ]]; then | |
echo "USAGE: nixify <path> [rev]" | |
echo "Path must exist and must be a directory" | |
echo "You can optionally provide the rev of nixpkgs-channels, defaults to refs/heads/nixos-19.09" | |
exit 1 | |
fi | |
if [[ ! -d "${1}" ]]; then | |
echo "ERR: ${1} is not directory" | |
exit 1 | |
fi | |
readonly here="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)" | |
readonly template_dir="$(cd "${here}/nixify-templates" && pwd)" | |
readonly location="$(cd "${1}" && pwd)" | |
readonly ref="${2:-refs/heads/nixos-19.09}" | |
cat >"${location}/.envrc" <<'EOF' | |
# Load environment variables from `nix-shell` and export it out. | |
# | |
# Usage: use_nix [-s <nix-expression>] [-w <path>] [-w <path>] ... | |
# -s nix-expression: The nix expression to use for building the shell environment. | |
# -w path: watch a file for changes. It can be specified multiple times. The | |
# shell specified with -s is automatically watched. | |
# | |
# If no nix-expression were given with -s, it will attempt to find and load | |
# the shell using the following files in order: shell.nix and default.nix. | |
# | |
# Example: | |
# - use_nix | |
# - use_nix -s shell.nix -w .nixpkgs-version.json | |
# | |
# The dependencies pulled by nix-shell are added to Nix's garbage collector | |
# roots, such that the environment remains persistent. | |
# | |
# Nix-shell is invoked only once per environment, and the output is cached for | |
# better performance. If any of the watched files change, then the environment | |
# is rebuilt. | |
# | |
# To remove old environments, and allow the GC to collect their dependencies: | |
# rm -f .direnv | |
use_nix() { | |
if ! validate_version; then | |
echo "This .envrc requires direnv version 2.18.2 or above." | |
exit 1 | |
fi | |
# define all local variables | |
local shell | |
local files_to_watch=() | |
local opt OPTARG OPTIND # define vars used by getopts locally | |
while getopts ":n:s:w:" opt; do | |
case "${opt}" in | |
s) | |
shell="${OPTARG}" | |
files_to_watch=("${files_to_watch[@]}" "${shell}") | |
;; | |
w) | |
files_to_watch=("${files_to_watch[@]}" "${OPTARG}") | |
;; | |
:) | |
fail "Invalid option: $OPTARG requires an argument" | |
;; | |
\?) | |
fail "Invalid option: $OPTARG" | |
;; | |
esac | |
done | |
shift $((OPTIND -1)) | |
if [[ -z "${shell}" ]]; then | |
if [[ -f shell.nix ]]; then | |
shell=shell.nix | |
files_to_watch=("${files_to_watch[@]}" shell.nix) | |
elif [[ -f default.nix ]]; then | |
shell=default.nix | |
files_to_watch=("${files_to_watch[@]}" default.nix) | |
else | |
fail "ERR: no shell was given" | |
fi | |
fi | |
local f | |
for f in "${files_to_watch[@]}"; do | |
if ! [[ -f "${f}" ]]; then | |
fail "cannot watch file ${f} because it does not exist" | |
fi | |
done | |
# compute the hash of all the files that makes up the development environment | |
local env_hash="$(hash_contents "${files_to_watch[@]}")" | |
# define the paths | |
local dir="$(direnv_layout_dir)" | |
local wd="${dir}/wd-${env_hash}" | |
local drv="${wd}/env.drv" | |
local dump="${wd}/dump.env" | |
# Generate the environment if we do not have one generated already. | |
if [[ ! -f "${drv}" ]]; then | |
mkdir -p "${wd}" | |
log_status "use nix: deriving new environment" | |
IN_NIX_SHELL=1 nix-instantiate --add-root "${drv}" --indirect "${shell}" > /dev/null | |
nix-store -r $(nix-store --query --references "${drv}") --add-root "${wd}/dep" --indirect > /dev/null | |
if [[ "${?}" -ne 0 ]] || [[ ! -f "${drv}" ]]; then | |
rm -rf "${wd}" | |
fail "use nix: was not able to derive the new environment. Please run 'direnv reload' to try again." | |
fi | |
log_status "use nix: updating cache" | |
nix-shell --pure "${drv}" --run "$(join_args "$direnv" dump bash)" > "${dump}" | |
if [[ "${?}" -ne 0 ]] || [[ ! -f "${dump}" ]] || ! grep -q IN_NIX_SHELL "${dump}"; then | |
rm -rf "${wd}" | |
fail "use nix: was not able to update the cache of the environment. Please run 'direnv reload' to try again." | |
fi | |
fi | |
for d in ${dir}/*; do | |
if [ "$wd" != "$d" ]; then | |
echo "Old direnv working directory: $d" | |
fi | |
done | |
# evaluate the dump created by nix-shell earlier, but have to merge the PATH | |
# with the current PATH | |
# NOTE: we eval the dump here as opposed to direnv_load it because we don't | |
# want to persist environment variables coming from the shell at the time of | |
# the dump. See https://github.com/direnv/direnv/issues/405 for context. | |
local path_backup="${PATH}" | |
eval $(cat "${dump}") | |
export PATH="${PATH}:${path_backup}" | |
# cleanup the environment of variables that are not requried, or are causing issues. | |
unset shellHook # when shellHook is present, then any nix-shell'd script will execute it! | |
# watch all the files we were asked to watch for the environment | |
for f in "${files_to_watch[@]}"; do | |
watch_file "${f}" | |
done | |
} | |
fail() { | |
log_error "${@}" | |
exit 1 | |
} | |
hash_contents() { | |
if has sha256sum; then | |
cat "${@}" | sha256sum | cut -c -64 | |
elif has md5sum; then | |
cat "${@}" | md5sum | cut -c -32 | |
elif has md5; then | |
cat "${@}" | md5 -q | |
fi | |
} | |
hash_file() { | |
if has sha256sum; then | |
sha256sum "${@}" | cut -c -64 | |
elif has md5sum; then | |
md5sum "${@}" | cut -c -32 | |
elif has md5; then | |
md5 -q "${@}" | |
fi | |
} | |
validate_version() { | |
local version="$("${direnv}" version)" | |
local major="$(echo "${version}" | cut -d. -f1)" | |
local minor="$(echo "${version}" | cut -d. -f2)" | |
local patch="$(echo "${version}" | cut -d. -f3)" | |
if [[ "${major}" -gt 2 ]]; then return 0; fi | |
if [[ "${major}" -eq 2 ]] && [[ "${minor}" -gt 18 ]]; then return 0; fi | |
if [[ "${major}" -eq 2 ]] && [[ "${minor}" -eq 18 ]] && [[ "${patch}" -ge 2 ]]; then return 0; fi | |
return 1 | |
} | |
use_nix -s shell.nix -w .nixpkgs-version.json | |
EOF | |
cat >"${location}/shell.nix" <<EOF | |
let | |
# Look here for information about how to generate \`nixpkgs-version.json\`. | |
# → https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs | |
pinnedVersion = builtins.fromJSON (builtins.readFile ./.nixpkgs-version.json); | |
pinnedPkgs = import (builtins.fetchGit { | |
inherit (pinnedVersion) url rev; | |
ref = "$(basename ${ref})"; | |
}) {}; | |
in | |
# This allows overriding pkgs by passing \`--arg pkgs ...\` | |
{ pkgs ? pinnedPkgs }: | |
with pkgs; | |
mkShell { | |
buildInputs = [ | |
# put packages here. | |
]; | |
} | |
EOF | |
nix-prefetch-git https://github.com/NixOS/nixpkgs-channels.git "${ref}" > "${location}/.nixpkgs-version.json" | |
chmod 644 ${location}/{.envrc,shell.nix,.nixpkgs-version.json} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment