-
-
Save dmjio/2f94e4f0884f8442f4d845cd19cb60c7 to your computer and use it in GitHub Desktop.
Nix release builder
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
# Function which takes a list of packages to install and creates a | |
# tarball which contains the full list of dependencies of those paths, | |
# and a script which will install them. | |
# | |
# For example, here's how you would create a tarball packaging up | |
# python3 and nodejs: | |
# | |
# | |
# let | |
# pkgs = import <nixpkgs> {}; | |
# mkTarball = import ./mkTarball.nix {inherit pkgs;}; | |
# in | |
# | |
# mkTarball { | |
# name = "python-and-node"; | |
# packagesToInstall = [pkgs.python3 pkgs.nodejs]; | |
# } | |
# | |
# | |
# The root package object (contains some essential things like nix | |
# and perl) | |
{pkgs}: | |
# Given a list of packages, creates a tarball containing the binaries for | |
# those packages and an installation script which will install all of them. | |
# Includes nix and a CA cert file by default (if no other packages are | |
# provided, only these will be installed). | |
{ | |
# The name of the projects being bundled. | |
name, | |
# The packages to include in this project. They will be copied into the | |
# nix store, but not installed into the user environment (~/.nix-profile). | |
packagesToInclude ? [], | |
# Packages to install into user environment in addition to nix. | |
packagesToInstall ? [], | |
# The compression type to use ("xz" or "gz") | |
compressionType ? "xz" | |
}: | |
let | |
# Grab the following objects from the base packages object: | |
inherit (pkgs) | |
# The nix executable | |
nix | |
# Runs the `pathsFromGraph` script below | |
perl | |
# For showing progress while tarring | |
pv | |
# A perl script that generates paths | |
pathsFromGraph | |
# Compresses stuff (obviously) | |
gnutar | |
# Provides the `uname` command | |
coreutils | |
# Will run a shell script and create the derivation | |
runCommand | |
# Root-level certificates used when nix is curling things. | |
cacert | |
# Contains low-level dependencies required when installing later. | |
stdenv; | |
inherit (pkgs.lib) zipListsWith concatStringsSep; | |
inherit (builtins) map; | |
# Tells us about the system (e.g. linux vs darwin). | |
system = builtins.currentSystem; | |
# Full list of packages to lump together. | |
packageList = packagesToInclude ++ packagesToInstall ++ [nix cacert stdenv]; | |
# Names of closure files that we will generate. | |
closures = map (pkg: "${pkg.name}-closure") packageList; | |
# Closures as they will appear in bash, a space-separated list. | |
closureArgs = concatStringsSep " " (map (x: "./${x}") closures); | |
compressor = if compressionType == "xz" then "xz -c -" | |
else if compressionType == "gz" then "gzip" | |
else throw "Invalid compression type: ${compressionType}"; | |
in | |
runCommand "${name}-binary-tarball" | |
{ | |
# Creates some files and populates them with the dependency closure of | |
# various expressions, which will be input into the perl `pathsFromGraph` | |
# invocation, below. | |
exportReferencesGraph = zipListsWith (a: b: [a b]) closures packageList; | |
buildInputs = [ perl coreutils pv ]; | |
packageNames = map (p: p.name) packageList; | |
meta.description = "Builds a self-contained Nix installer for ${system}"; | |
} | |
'' | |
storePaths=$(${perl}/bin/perl ${pathsFromGraph} ${closureArgs}) | |
printRegistration=1 ${perl}/bin/perl ${pathsFromGraph} \ | |
${closureArgs} > $TMPDIR/reginfo | |
# `dest` is the folder in which the nix store appears. | |
# All of these files will be read by the install script. | |
dirname $NIX_STORE > $TMPDIR/dest_path | |
# Store the paths of nix and the cacert; these get special treatment. | |
echo ${nix} > $TMPDIR/nix_path | |
echo ${cacert} > $TMPDIR/cacert_path | |
# Store the system this was built on. | |
echo "$(uname -s).$(uname -m)" > $TMPDIR/expected_system | |
# Make these scalar files read-only. | |
chmod 440 $TMPDIR/dest_path $TMPDIR/reginfo \ | |
$TMPDIR/cacert_path $TMPDIR/nix_path $TMPDIR/expected_system | |
# Store the paths of extra packages to install. | |
mkdir -p $TMPDIR/also_install | |
${concatStringsSep "\n" (map (p: '' | |
echo ${p} > $TMPDIR/also_install/${p.name} | |
chmod 440 $TMPDIR/also_install/${p.name} | |
'') packagesToInstall)} | |
cp ${./installation.sh} $TMPDIR/install | |
chmod +x $TMPDIR/install | |
mkdir -p $out | |
directory=${name}-${system} | |
tarball=$out/$directory.tar.${compressionType} | |
echo "Building release tarball in ${compressionType} format" | |
files_to_compress="$TMPDIR/install $TMPDIR/reginfo $TMPDIR/dest_path | |
$TMPDIR/nix_path $TMPDIR/cacert_path | |
$TMPDIR/also_install $TMPDIR/expected_system | |
$storePaths" | |
intermediate=$TMPDIR/intermediate.tar | |
tar -cf $intermediate \ | |
--owner=0 --group=0 --mode=u+rw,uga+r -P \ | |
--absolute-names \ | |
--hard-dereference \ | |
--transform "s,$TMPDIR/install,$directory/install," \ | |
--transform "s,$TMPDIR/dest_path,$directory/dest_path," \ | |
--transform "s,$TMPDIR/reginfo,$directory/reginfo," \ | |
--transform "s,$TMPDIR/nix_path,$directory/nix_path," \ | |
--transform "s,$TMPDIR/cacert_path,$directory/cacert_path," \ | |
--transform "s,$TMPDIR/also_install,$directory/also_install," \ | |
--transform "s,$TMPDIR/expected_system,$directory/expected_system," \ | |
--transform "s,$NIX_STORE,$directory/store,S" \ | |
$files_to_compress | |
size="$(du -sb $intermediate | awk '{print $1}')" | |
cat $intermediate | pv -ptef -s $size | ${compressor} > $tarball | |
'' |
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 bash | |
# This script will install nix and a set of dependencies. You can see what | |
# other programs will be installed by looking at the file names in the | |
# ./package_paths directory. | |
set -e | |
# The argument passed to bash to run this script, e.g. 'nix-1.8/install'. | |
script=$0 | |
# The directory the script is in, e.g. `nix-1.8`. | |
dir=$(dirname $script) | |
# The current user running this script. | |
user=$(command whoami) | |
# Nix will create the following three files when it builds this tarball. | |
# The prefix into which the nix store and state folders will be installed. | |
# E.g., `/nix` or `/opt/nix` | |
dest=$(cat $dir/dest_path) | |
# The location within the nix store of the nix bin/lib/etc directories. | |
# E.g. `/nix/store/845yoiefnwe9084rtgunsdoifut-nix-1.8` | |
nix=$(cat $dir/nix_path) | |
# The location within the nix store of a root-level CA certificate file. | |
# E.g `/nix/store/394utnldfkutuw04893u5in3w4-cacert-20140417` | |
cacert=$(cat $dir/cacert_path) | |
# The system this package is meant to be installed onto (e.g. 64-bit linux). | |
expect_system=$(cat $dir/expected_system) | |
this_system="$(uname -s).$(uname -m)" | |
if [ $expect_system != $this_system ]; then | |
echo "Incompatible system:" | |
echo "This build is meant for $expect_system, but this system is $this_system." >&2 | |
exit 1 | |
fi | |
try_or_abort() { | |
# Tries a command, exits with a message if the command fails. | |
errmsg=${2:-"Command '$1' failed"} | |
command bash -c "$1" || { | |
echo "${script}: ${errmsg}" >&2 | |
exit 1 | |
} | |
} | |
try_or_abort '! type -pf nix-env 2>&1 >/dev/null' \ | |
"Nix has already been installed." | |
try_or_abort 'test "$(id -u)" != 0' "Refuse to install as root." | |
try_or_abort "mkdir -p ${dest}" "Could not create nix directory $dest." | |
try_or_abort "chmod 0755 ${dest}" "Couldn't change permissions of ${dest}." | |
try_or_abort "test -w ${dest}" "${dest} is not writable by ${user}." | |
echo "Installing Nix..." | |
mkdir -p ${dest}/store | |
echo -n "Copying Nix objects to ${dest}/store..." | |
for i in $(ls $dir/store); do | |
echo -n "." | |
i_tmp="${dest}/store/$i.$$" | |
if [ -e "$i_tmp" ]; then | |
rm -rf "$i_tmp" | |
fi | |
if ! [ -e "${dest}/store/$i" ]; then | |
cp -Rp "${dir}/store/$i" "$i_tmp" | |
mv "$i_tmp" "${dest}/store/$i" | |
fi | |
done | |
echo " OK" | |
echo "Initializing Nix database..." | |
try_or_abort "${nix}/bin/nix-store --init" \ | |
"failed to initialize the Nix database" | |
try_or_abort "${nix}/bin/nix-store --load-db < ${dir}/reginfo" \ | |
"unable to register valid paths" | |
source ${nix}/etc/profile.d/nix.sh | |
echo "Installing nix..." | |
try_or_abort "${nix}/bin/nix-env -i ${nix} --option use-binary-caches false" \ | |
"unable to install Nix into your default profile" | |
echo "Installing SSL certificate bundle..." | |
${nix}/bin/nix-env -i "$cacert" | |
export SSL_CERT_FILE="$HOME/.nix-profile/etc/ca-bundle.crt" | |
for pkg_path_file in $dir/also_install/*; do | |
echo "Installing $(basename pkg_path_file)..." | |
${nix}/bin/nix-env -i "$(cat $pkg_path_file)" --option use-binary-caches false | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment