mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
Ensure that the version of Playwright installed with the Nix flake is equal to the one specified in `site/package.json.` -- This assertion ensures that `pnpm playwright:install` will not attempt to download newer browser versions not present in the Nix image, fixing the startup script and reducing the startup time, as `pnpm playwright:install` will not download or install anything. We also pre-install the required Playwright web browsers in the dogfood Dockerfile. This change prevents us from redownloading system dependencies and Google Chrome each time a workspace starts. Change-Id: I8cc78e842f7d0b1d2a90a4517a186a03636c5559 Signed-off-by: Thomas Kosiewski <tk@coder.com> Signed-off-by: Thomas Kosiewski <tk@coder.com>
394 lines
13 KiB
Nix
394 lines
13 KiB
Nix
# (ThomasK33): Inlined the relevant dockerTools functions, so that we can
|
|
# set the maxLayers attribute on the attribute set passed
|
|
# to the buildNixShellImage function.
|
|
#
|
|
# I'll create an upstream PR to nixpkgs with those changes, making this
|
|
# eventually unnecessary and ripe for removal.
|
|
{
|
|
lib,
|
|
dockerTools,
|
|
devShellTools,
|
|
bashInteractive,
|
|
fakeNss,
|
|
runCommand,
|
|
writeShellScriptBin,
|
|
writeText,
|
|
writeTextFile,
|
|
writeTextDir,
|
|
cacert,
|
|
storeDir ? builtins.storeDir,
|
|
pigz,
|
|
zstd,
|
|
stdenv,
|
|
glibc,
|
|
sudo,
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
optionalString
|
|
;
|
|
|
|
inherit (devShellTools)
|
|
valueToString
|
|
;
|
|
|
|
inherit (dockerTools)
|
|
streamLayeredImage
|
|
usrBinEnv
|
|
caCertificates
|
|
;
|
|
|
|
# This provides /bin/sh, pointing to bashInteractive.
|
|
# The use of bashInteractive here is intentional to support cases like `docker run -it <image_name>`, so keep these use cases in mind if making any changes to how this works.
|
|
binSh = runCommand "bin-sh" { } ''
|
|
mkdir -p $out/bin
|
|
ln -s ${bashInteractive}/bin/bash $out/bin/sh
|
|
ln -s ${bashInteractive}/bin/bash $out/bin/bash
|
|
'';
|
|
|
|
etcNixConf = writeTextDir "etc/nix/nix.conf" ''
|
|
experimental-features = nix-command flakes
|
|
'';
|
|
|
|
etcPamdSudoFile = writeText "pam-sudo" ''
|
|
# Allow root to bypass authentication (optional)
|
|
auth sufficient pam_rootok.so
|
|
|
|
# For all users, always allow auth
|
|
auth sufficient pam_permit.so
|
|
|
|
# Do not perform any account management checks
|
|
account sufficient pam_permit.so
|
|
|
|
# No password management here (only needed if you are changing passwords)
|
|
# password requisite pam_unix.so nullok yescrypt
|
|
|
|
# Keep session logging if desired
|
|
session required pam_unix.so
|
|
'';
|
|
|
|
etcPamdSudo = runCommand "etc-pamd-sudo" { } ''
|
|
mkdir -p $out/etc/pam.d/
|
|
ln -s ${etcPamdSudoFile} $out/etc/pam.d/sudo
|
|
ln -s ${etcPamdSudoFile} $out/etc/pam.d/su
|
|
'';
|
|
|
|
compressors = {
|
|
none = {
|
|
ext = "";
|
|
nativeInputs = [ ];
|
|
compress = "cat";
|
|
decompress = "cat";
|
|
};
|
|
gz = {
|
|
ext = ".gz";
|
|
nativeInputs = [ pigz ];
|
|
compress = "pigz -p$NIX_BUILD_CORES -nTR";
|
|
decompress = "pigz -d -p$NIX_BUILD_CORES";
|
|
};
|
|
zstd = {
|
|
ext = ".zst";
|
|
nativeInputs = [ zstd ];
|
|
compress = "zstd -T$NIX_BUILD_CORES";
|
|
decompress = "zstd -d -T$NIX_BUILD_CORES";
|
|
};
|
|
};
|
|
compressorForImage =
|
|
compressor: imageName:
|
|
compressors.${compressor}
|
|
or (throw "in docker image ${imageName}: compressor must be one of: [${toString builtins.attrNames compressors}]");
|
|
|
|
streamNixShellImage =
|
|
{
|
|
drv,
|
|
name ? drv.name + "-env",
|
|
tag ? null,
|
|
uid ? 1000,
|
|
gid ? 1000,
|
|
homeDirectory ? "/build",
|
|
shell ? bashInteractive + "/bin/bash",
|
|
command ? null,
|
|
run ? null,
|
|
maxLayers ? 100,
|
|
uname ? "nixbld",
|
|
releaseName ? "0.0.0",
|
|
}:
|
|
assert lib.assertMsg (!(drv.drvAttrs.__structuredAttrs or false))
|
|
"streamNixShellImage: Does not work with the derivation ${drv.name} because it uses __structuredAttrs";
|
|
assert lib.assertMsg (
|
|
command == null || run == null
|
|
) "streamNixShellImage: Can't specify both command and run";
|
|
let
|
|
|
|
# A binary that calls the command to build the derivation
|
|
builder = writeShellScriptBin "buildDerivation" ''
|
|
exec ${lib.escapeShellArg (valueToString drv.drvAttrs.builder)} ${lib.escapeShellArgs (map valueToString drv.drvAttrs.args)}
|
|
'';
|
|
|
|
staticPath = "${dirOf shell}:${
|
|
lib.makeBinPath (
|
|
(lib.flatten [
|
|
builder
|
|
drv.buildInputs
|
|
])
|
|
++ [ "/usr" ]
|
|
)
|
|
}";
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/nix-build/nix-build.cc#L493-L526
|
|
rcfile = writeText "nix-shell-rc" ''
|
|
unset PATH
|
|
dontAddDisableDepTrack=1
|
|
# TODO: https://github.com/NixOS/nix/blob/2.8.0/src/nix-build/nix-build.cc#L506
|
|
[ -e $stdenv/setup ] && source $stdenv/setup
|
|
PATH=${staticPath}:"$PATH"
|
|
SHELL=${lib.escapeShellArg shell}
|
|
BASH=${lib.escapeShellArg shell}
|
|
set +e
|
|
[ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '
|
|
if [ "$(type -t runHook)" = function ]; then
|
|
runHook shellHook
|
|
fi
|
|
unset NIX_ENFORCE_PURITY
|
|
shopt -u nullglob
|
|
shopt -s execfail
|
|
${optionalString (command != null || run != null) ''
|
|
${optionalString (command != null) command}
|
|
${optionalString (run != null) run}
|
|
exit
|
|
''}
|
|
'';
|
|
|
|
etcSudoers = writeTextDir "etc/sudoers" ''
|
|
root ALL=(ALL) ALL
|
|
${toString uname} ALL=(ALL) NOPASSWD:ALL
|
|
'';
|
|
|
|
# Add our Docker init script
|
|
dockerInit = writeTextFile {
|
|
name = "initd-docker";
|
|
destination = "/etc/init.d/docker";
|
|
executable = true;
|
|
|
|
text = ''
|
|
#!/usr/bin/env sh
|
|
### BEGIN INIT INFO
|
|
# Provides: docker
|
|
# Required-Start: $remote_fs $syslog
|
|
# Required-Stop: $remote_fs $syslog
|
|
# Default-Start: 2 3 4 5
|
|
# Default-Stop: 0 1 6
|
|
# Short-Description: Start and stop Docker daemon
|
|
# Description: This script starts and stops the Docker daemon.
|
|
### END INIT INFO
|
|
|
|
case "$1" in
|
|
start)
|
|
echo "Starting dockerd"
|
|
SSL_CERT_FILE="${cacert}/etc/ssl/certs/ca-bundle.crt" dockerd --group=${toString gid} &
|
|
;;
|
|
stop)
|
|
echo "Stopping dockerd"
|
|
killall dockerd
|
|
;;
|
|
restart)
|
|
$0 stop
|
|
$0 start
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {start|stop|restart}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
exit 0
|
|
'';
|
|
};
|
|
|
|
etcReleaseName = writeTextDir "etc/coderniximage-release" ''
|
|
${releaseName}
|
|
'';
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/globals.hh#L464-L465
|
|
sandboxBuildDir = "/build";
|
|
|
|
drvEnv =
|
|
devShellTools.unstructuredDerivationInputEnv { inherit (drv) drvAttrs; }
|
|
// devShellTools.derivationOutputEnv {
|
|
outputList = drv.outputs;
|
|
outputMap = drv;
|
|
};
|
|
|
|
# Environment variables set in the image
|
|
envVars =
|
|
{
|
|
|
|
# Root certificates for internet access
|
|
SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
|
|
NIX_SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1027-L1030
|
|
# PATH = "/path-not-set";
|
|
# Allows calling bash and `buildDerivation` as the Cmd
|
|
PATH = staticPath;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1032-L1038
|
|
HOME = homeDirectory;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1040-L1044
|
|
NIX_STORE = storeDir;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1046-L1047
|
|
# TODO: Make configurable?
|
|
NIX_BUILD_CORES = "1";
|
|
|
|
# Make sure we get the libraries for C and C++ in.
|
|
LD_LIBRARY_PATH = lib.makeLibraryPath [ stdenv.cc.cc ];
|
|
}
|
|
// drvEnv
|
|
// rec {
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1008-L1010
|
|
NIX_BUILD_TOP = sandboxBuildDir;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1012-L1013
|
|
TMPDIR = TMP;
|
|
TEMPDIR = TMP;
|
|
TMP = "/tmp";
|
|
TEMP = TMP;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1015-L1019
|
|
PWD = homeDirectory;
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1071-L1074
|
|
# We don't set it here because the output here isn't handled in any special way
|
|
# NIX_LOG_FD = "2";
|
|
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/libstore/build/local-derivation-goal.cc#L1076-L1077
|
|
TERM = "xterm-256color";
|
|
};
|
|
|
|
in
|
|
streamLayeredImage {
|
|
inherit name tag maxLayers;
|
|
contents = [
|
|
binSh
|
|
usrBinEnv
|
|
caCertificates
|
|
etcNixConf
|
|
etcSudoers
|
|
etcPamdSudo
|
|
etcReleaseName
|
|
(fakeNss.override {
|
|
# Allows programs to look up the build user's home directory
|
|
# https://github.com/NixOS/nix/blob/ffe155abd36366a870482625543f9bf924a58281/src/libstore/build/local-derivation-goal.cc#L906-L910
|
|
# Slightly differs however: We use the passed-in homeDirectory instead of sandboxBuildDir.
|
|
# We're doing this because it's arguably a bug in Nix that sandboxBuildDir is used here: https://github.com/NixOS/nix/issues/6379
|
|
extraPasswdLines = [
|
|
"${toString uname}:x:${toString uid}:${toString gid}:Build user:${homeDirectory}:${lib.escapeShellArg shell}"
|
|
];
|
|
extraGroupLines = [
|
|
"${toString uname}:!:${toString gid}:"
|
|
"docker:!:${toString (builtins.sub gid 1)}:${toString uname}"
|
|
];
|
|
})
|
|
dockerInit
|
|
];
|
|
|
|
fakeRootCommands = ''
|
|
# Effectively a single-user installation of Nix, giving the user full
|
|
# control over the Nix store. Needed for building the derivation this
|
|
# shell is for, but also in case one wants to use Nix inside the
|
|
# image
|
|
mkdir -p ./nix/{store,var/nix} ./etc/nix
|
|
chown -R ${toString uid}:${toString gid} ./nix ./etc/nix
|
|
|
|
# Gives the user control over the build directory
|
|
mkdir -p .${sandboxBuildDir}
|
|
chown -R ${toString uid}:${toString gid} .${sandboxBuildDir}
|
|
|
|
mkdir -p .${homeDirectory}
|
|
chown -R ${toString uid}:${toString gid} .${homeDirectory}
|
|
|
|
mkdir -p ./tmp
|
|
chown -R ${toString uid}:${toString gid} ./tmp
|
|
|
|
mkdir -p ./etc/skel
|
|
chown -R ${toString uid}:${toString gid} ./etc/skel
|
|
|
|
# Create traditional /lib or /lib64 as needed.
|
|
# For aarch64 (arm64):
|
|
if [ -e "${glibc}/lib/ld-linux-aarch64.so.1" ]; then
|
|
mkdir -p ./lib
|
|
ln -s "${glibc}/lib/ld-linux-aarch64.so.1" ./lib/ld-linux-aarch64.so.1
|
|
fi
|
|
|
|
# For x86_64:
|
|
if [ -e "${glibc}/lib64/ld-linux-x86-64.so.2" ]; then
|
|
mkdir -p ./lib64
|
|
ln -s "${glibc}/lib64/ld-linux-x86-64.so.2" ./lib64/ld-linux-x86-64.so.2
|
|
fi
|
|
|
|
# Copy sudo from the Nix store to a "normal" path in the container
|
|
mkdir -p ./usr/bin
|
|
cp ${sudo}/bin/sudo ./usr/bin/sudo
|
|
|
|
# Ensure root owns it & set setuid bit
|
|
chown 0:0 ./usr/bin/sudo
|
|
chmod 4755 ./usr/bin/sudo
|
|
|
|
chown root:root ./etc/pam.d/sudo
|
|
chown root:root ./etc/pam.d/su
|
|
chown root:root ./etc/sudoers
|
|
|
|
# Create /var/run and chown it so docker command
|
|
# doesnt encounter permission issues.
|
|
mkdir -p ./var/run/
|
|
chown -R ${toString uid}:${toString gid} ./var/run/
|
|
'';
|
|
|
|
# Run this image as the given uid/gid
|
|
config.User = "${toString uid}:${toString gid}";
|
|
config.Cmd =
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/nix-build/nix-build.cc#L185-L186
|
|
# https://github.com/NixOS/nix/blob/2.8.0/src/nix-build/nix-build.cc#L534-L536
|
|
if run == null then
|
|
[
|
|
shell
|
|
"--rcfile"
|
|
rcfile
|
|
]
|
|
else
|
|
[
|
|
shell
|
|
rcfile
|
|
];
|
|
config.WorkingDir = homeDirectory;
|
|
config.Env = lib.mapAttrsToList (name: value: "${name}=${value}") envVars;
|
|
};
|
|
in
|
|
{
|
|
inherit streamNixShellImage;
|
|
|
|
# This function streams a docker image that behaves like a nix-shell for a derivation
|
|
# Docs: doc/build-helpers/images/dockertools.section.md
|
|
# Tests: nixos/tests/docker-tools-nix-shell.nix
|
|
|
|
# Wrapper around streamNixShellImage to build an image from the result
|
|
# Docs: doc/build-helpers/images/dockertools.section.md
|
|
# Tests: nixos/tests/docker-tools-nix-shell.nix
|
|
buildNixShellImage =
|
|
{
|
|
drv,
|
|
compressor ? "gz",
|
|
...
|
|
}@args:
|
|
let
|
|
stream = streamNixShellImage (builtins.removeAttrs args [ "compressor" ]);
|
|
compress = compressorForImage compressor drv.name;
|
|
in
|
|
runCommand "${drv.name}-env.tar${compress.ext}" {
|
|
inherit (stream) imageName;
|
|
passthru = { inherit (stream) imageTag; };
|
|
nativeBuildInputs = compress.nativeInputs;
|
|
} "${stream} | ${compress.compress} > $out";
|
|
}
|