Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: Add --build-on flag with settings: auto, local, remote #461

Merged
merged 2 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ data has been migrated.
- Source Machine:

- Can be any machine with Nix installed, e.g. a NixOS machine.
- Should be able to build nix derivations for the target platform. Otherwise
`--build-on-remote` can be used.

- Target Machine:

Expand Down
5 changes: 5 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@ Options:
build the closure on the remote machine instead of locally and copy-closuring it
* --vm-test
build the system and test the disk configuration inside a VM without installing it to the target.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
```
3 changes: 0 additions & 3 deletions docs/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
- NixOS
- Windows systems using WSL2.

_Note_: Should be able to build nix derivations for the target platform.
Otherwise `--build-on-remote` can be used.

2. **Nix Installation:** If Nix is not yet installed on your system, refer to
the [nix installation page](https://nixos.org/download#download-nix).

Expand Down
93 changes: 86 additions & 7 deletions src/nixos-anywhere.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fi
sshConnection=
postKexecSshPort=22
buildOnRemote=n
buildOn=auto
envPassword=n

# Facts set by get-facts.sh
Expand Down Expand Up @@ -126,8 +127,11 @@ Options:
* --disko-mode disko|mount|format
set the disko mode to format, mount or destroy. Default is disko.
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
mount: mount the partition at the specified root-mountpoint
format: create partition tables, zpools, lvms, raids and filesystems (Experimental: Can be run increntally, but use with caution and good backups)
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
USAGE
}

Expand All @@ -143,6 +147,7 @@ step() {
parseArgs() {
local substituteOnDestination=y
local printBuildLogs=n
local buildOnRemote=n
while [[ $# -gt 0 ]]; do
case "$1" in
-f | --flake)
Expand Down Expand Up @@ -227,6 +232,18 @@ parseArgs() {
;;
esac

shift
;;
--build-on)
case "$2" in
auto | local | remote)
buildOn=$2
;;
*)
abort "Supported values for --build-on are auto, local and remote. Unknown mode : $2"
;;
esac

shift
;;
--extra-files)
Expand Down Expand Up @@ -281,7 +298,9 @@ parseArgs() {
substituteOnDestination=n
;;
--build-on-remote)
echo "WARNING: --build-on-remote is deprecated, use --build-on remote instead" 2>&1
buildOnRemote=y
buildOn="remote"
;;
--env-password)
envPassword=y
Expand Down Expand Up @@ -313,6 +332,10 @@ parseArgs() {
abort "ssh-host must be set"
fi

if [[ $buildOn == "local" ]] && [[ $buildOnRemote == "y" ]]; then
abort "Conflicting flags: --build-on local and --build-on-remote used."
fi

if [[ -n ${flake} ]]; then
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
Expand Down Expand Up @@ -364,7 +387,7 @@ runVmTest() {
exit 1
fi

if [[ ${buildOnRemote} == "y" ]]; then
if [[ ${buildOn} == "remote" ]]; then
echo "--vm-test is not supported with --build-on-remote" >&2
exit 1
fi
Expand Down Expand Up @@ -450,6 +473,47 @@ importFacts() {
done
}

checkBuildLocally() {
local system extraPlatforms machineSystem
system="$(nix --extra-experimental-features 'nix-command flakes' config show system)"
extraPlatforms="$(nix --extra-experimental-features 'nix-command flakes' config show extra-platforms)"

if [[ $# -gt 0 ]]; then
machineSystem=$1
elif [[ -n ${nixosSystem} ]]; then
machineSystem="$(cat "${nixosSystem}"/system)"
else
machineSystem="$(nix --extra-experimental-features 'nix-command flakes' eval --raw "${flake}"#"${flakeAttr}".pkgs.system 2>/dev/null || echo "unknown")"
if [[ ${machineSystem} == "unknown" ]]; then
buildOn=auto
return
fi
fi

if [[ ${system} == "${machineSystem}" ]]; then
buildOn=local
return
fi

if [[ ${extraPlatforms} == "*${machineSystem}*" ]]; then
buildOn=local
return
fi

local entropy
entropy="$(date +'%Y%m%d%H%M%S')"
if nix build \
-L \
"${nixOptions[@]}" \
--expr \
"derivation { system = \"$system\"; name = \"env-$entropy\"; builder = \"/bin/sh\"; args = [ \"-c\" \"echo > \$out\" ]; }"; then
# The local build failed
buildOn=local
fi

buildOn=remote
}

generateHardwareConfig() {
local maybeSudo="$maybeSudo"
mkdir -p "$(dirname "$hardwareConfigPath")"
Expand Down Expand Up @@ -557,7 +621,7 @@ runDisko() {
done
if [[ -n ${diskoScript} ]]; then
nixCopy --to "ssh://$sshConnection" "$diskoScript"
elif [[ ${buildOnRemote} == "y" ]]; then
elif [[ ${buildOn} == "remote" ]]; then
step Building disko script
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
Expand All @@ -579,7 +643,7 @@ nixosInstall() {
if [[ -n ${nixosSystem} ]]; then
step Uploading the system closure
nixCopy --to "ssh://$sshConnection?remote-store=local?root=/mnt" "$nixosSystem"
elif [[ ${buildOnRemote} == "y" ]]; then
elif [[ ${buildOn} == "remote" ]]; then
step Building the system closure
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
Expand Down Expand Up @@ -644,9 +708,13 @@ main() {
exit 0
fi

if [[ ${buildOn} == "auto" ]]; then
checkBuildLocally
fi

# parse flake nixos-install style syntax, get the system attr
if [[ -n ${flake} ]]; then
if [[ ${buildOnRemote} == "n" ]] && [[ ${hardwareConfigBackend} == "none" ]]; then
if [[ ${buildOn} == "local" ]] && [[ ${hardwareConfigBackend} == "none" ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoMode}Script")
fi
Expand Down Expand Up @@ -708,7 +776,18 @@ main() {
generateHardwareConfig
fi

if [[ ${buildOnRemote} == "n" ]] && [[ -n ${flake} ]] && [[ ${hardwareConfigBackend} != "none" ]]; then
# Before we do not have a valid hardware configuration we don't know the machine system
if [[ ${buildOn} == "auto" ]]; then
local remoteSystem
remoteSystem=$(runSshNoTty -o ConnectTimeout=10 nix --extra-experimental-features nix-command config show system)
checkBuildLocally "${remoteSystem}"
# if we cannot figure it out at this point, we will build on the remote host
if [[ ${buildOn} == "auto" ]]; then
buildOn=remote
fi
fi

if [[ ${buildOn} != "remote" ]] && [[ -n ${flake} ]] && [[ -z ${diskoScript} ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoMode}Script")
fi
Expand Down