Merge pull request #23 from cachix/macos

MacOS fixes
This commit is contained in:
Domen Kožar 2020-02-26 13:06:39 +03:00 committed by GitHub
commit f57eb6b95e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 30 deletions

102
lib/create-darwin-volume.sh Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env bash
set -e
root_disks() {
diskutil list -plist /
}
apfs_volumes_for() {
disk=$1
diskutil apfs list -plist "$disk"
}
disk_identifier() {
xpath "/plist/dict/key[text()='WholeDisks']/following-sibling::array[1]/string/text()" 2>/dev/null
}
volume_get() {
key=$1 i=$2
xpath "/plist/dict/array/dict/key[text()='Volumes']/following-sibling::array/dict[$i]/key[text()='$key']/following-sibling::string[1]/text()" 2> /dev/null
}
find_nix_volume() {
disk=$1
i=1
volumes=$(apfs_volumes_for "$disk")
while true; do
name=$(echo "$volumes" | volume_get "Name" "$i")
if [ -z "$name" ]; then
break
fi
case "$name" in
[Nn]ix*)
echo "$name"
break
;;
esac
i=$((i+1))
done
}
test_fstab() {
grep -q "/nix" /etc/fstab 2>/dev/null
}
test_synthetic_conf() {
grep -q "^nix" /etc/synthetic.conf 2>/dev/null
}
test_nix() {
test -d "/nix"
}
main() {
(
echo ""
echo " ------------------------------------------------------------------ "
echo " | This installer will create a volume for the nix store and |"
echo " | configure it to mount at /nix. Follow these steps to uninstall. |"
echo " ------------------------------------------------------------------ "
echo ""
echo " 1. Remove the entry from fstab using 'sudo vifs'"
echo " 2. Destroy the data volume using 'diskutil apfs deleteVolume'"
echo " 3. Delete /etc/synthetic.conf"
echo ""
) >&2
if [ -L "/nix" ]; then
echo "error: /nix is a symlink, please remove it or edit synthetic.conf (requires reboot)" >&2
echo " /nix -> $(readlink "/nix")" >&2
exit 2
fi
if ! test_synthetic_conf; then
echo "Configuring /etc/synthetic.conf..." >&2
echo nix | sudo tee /etc/synthetic.conf
/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B
fi
if ! test_nix; then
echo "Creating mountpoint for /nix..." >&2
sudo mkdir /nix
fi
disk=$(root_disks | disk_identifier)
volume=$(find_nix_volume "$disk")
if [ -z "$volume" ]; then
echo "Creating a Nix Store volume..." >&2
sudo diskutil apfs addVolume "$disk" APFS 'Nix Store' -mountpoint /nix
volume="Nix Store"
else
echo "Using existing '$volume' volume" >&2
fi
if ! test_fstab; then
echo "Configuring /etc/fstab..." >&2
label=$(echo "$volume" | sed 's/ /\\040/g')
printf "\$a\nLABEL=%s /nix apfs rw\n.\nwq\n" "$label" | EDITOR=ed sudo vifs
sudo defaults write /Library/Preferences/SystemConfiguration/autodiskmount AutomountDisksWithoutUserLogin -bool true
fi
}
main "$@"

View file

@ -19,7 +19,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const tc = __importStar(require("@actions/tool-cache")); const tc = __importStar(require("@actions/tool-cache"));
const child_process_1 = require("child_process");
const os_1 = require("os"); const os_1 = require("os");
const process_1 = require("process");
const net_1 = require("net");
function nixConf() { function nixConf() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Workaround a segfault: https://github.com/NixOS/nix/issues/2733 // Workaround a segfault: https://github.com/NixOS/nix/issues/2733
@ -35,22 +38,14 @@ function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const PATH = process.env.PATH; const PATH = process.env.PATH;
const INSTALL_PATH = '/opt/nix';
yield nixConf(); yield nixConf();
// Catalina workaround https://github.com/NixOS/nix/issues/2925 // Catalina workaround https://github.com/NixOS/nix/issues/2925
if (os_1.type() == "Darwin") { if (os_1.type() == "Darwin") {
yield exec.exec("sudo", ["sh", "-c", `echo \"nix\t${INSTALL_PATH}\" >> /etc/synthetic.conf`]); child_process_1.execFileSync(`${__dirname}/create-darwin-volume.sh`, { stdio: 'inherit' });
yield exec.exec("sudo", ["sh", "-c", `mkdir -m 0755 ${INSTALL_PATH} && chown runner ${INSTALL_PATH}`]);
yield exec.exec("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util", ["-B"]);
// Needed for sudo to pass NIX_IGNORE_SYMLINK_STORE
yield exec.exec("sudo", ["sh", "-c", "echo 'Defaults env_keep += NIX_IGNORE_SYMLINK_STORE' >> /etc/sudoers"]);
core.exportVariable('NIX_IGNORE_SYMLINK_STORE', "1");
// Needed for nix-daemon installation
yield exec.exec("sudo", ["launchctl", "setenv", "NIX_IGNORE_SYMLINK_STORE", "1"]);
} }
// Needed due to multi-user being too defensive // Needed due to multi-user being too defensive
core.exportVariable('ALLOW_PREEXISTING_INSTALLATION', "1"); core.exportVariable('ALLOW_PREEXISTING_INSTALLATION', "1");
// TODO: retry due to all the things that go wrong // TODO: retry due to all the things that can go wrong
const nixInstall = yield tc.downloadTool('https://nixos.org/nix/install'); const nixInstall = yield tc.downloadTool('https://nixos.org/nix/install');
yield exec.exec("sh", [nixInstall, "--daemon"]); yield exec.exec("sh", [nixInstall, "--daemon"]);
// write nix.conf again as installation overwrites it, reload the daemon to pick up changes // write nix.conf again as installation overwrites it, reload the daemon to pick up changes
@ -63,9 +58,7 @@ function run() {
// macOS needs certificates hints // macOS needs certificates hints
core.exportVariable('NIX_SSL_CERT_FILE', '/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt'); core.exportVariable('NIX_SSL_CERT_FILE', '/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt');
// TODO: nc doesn't work correctly on macOS :( // TODO: nc doesn't work correctly on macOS :(
//await exec.exec("sh", ["-c", "while ! nc -zU /nix/var/nix/daemon-socket/socket; do sleep 0.5; done"]); yield awaitSocket();
// macOS needs time to reload the daemon :(
yield exec.exec("sleep", ["10"]);
} }
} }
catch (error) { catch (error) {
@ -74,4 +67,17 @@ function run() {
} }
}); });
} }
function awaitSocket() {
return __awaiter(this, void 0, void 0, function* () {
const daemonSocket = net_1.createConnection({ path: '/nix/var/nix/daemon-socket/socket' });
daemonSocket.on('error', () => __awaiter(this, void 0, void 0, function* () {
console.log('Waiting for daemon socket to be available, reconnecting...');
yield new Promise(resolve => setTimeout(resolve, 500));
yield awaitSocket();
}));
daemonSocket.on('connect', () => {
process_1.exit(0);
});
});
}
run(); run();

View file

@ -1,7 +1,10 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import {execFileSync} from 'child_process';
import {type} from 'os'; import {type} from 'os';
import {exit} from 'process';
import {createConnection} from 'net';
async function nixConf() { async function nixConf() {
// Workaround a segfault: https://github.com/NixOS/nix/issues/2733 // Workaround a segfault: https://github.com/NixOS/nix/issues/2733
@ -18,27 +21,18 @@ async function nixConf() {
async function run() { async function run() {
try { try {
const PATH = process.env.PATH; const PATH = process.env.PATH;
const INSTALL_PATH = '/opt/nix';
await nixConf(); await nixConf();
// Catalina workaround https://github.com/NixOS/nix/issues/2925 // Catalina workaround https://github.com/NixOS/nix/issues/2925
if (type() == "Darwin") { if (type() == "Darwin") {
await exec.exec("sudo", ["sh", "-c", `echo \"nix\t${INSTALL_PATH}\" >> /etc/synthetic.conf`]); execFileSync(`${__dirname}/create-darwin-volume.sh`, { stdio: 'inherit' });
await exec.exec("sudo", ["sh", "-c", `mkdir -m 0755 ${INSTALL_PATH} && chown runner ${INSTALL_PATH}`]);
await exec.exec("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util", ["-B"]);
// Needed for sudo to pass NIX_IGNORE_SYMLINK_STORE
await exec.exec("sudo", ["sh", "-c", "echo 'Defaults env_keep += NIX_IGNORE_SYMLINK_STORE' >> /etc/sudoers"]);
core.exportVariable('NIX_IGNORE_SYMLINK_STORE', "1");
// Needed for nix-daemon installation
await exec.exec("sudo", ["launchctl", "setenv", "NIX_IGNORE_SYMLINK_STORE", "1"]);
} }
// Needed due to multi-user being too defensive // Needed due to multi-user being too defensive
core.exportVariable('ALLOW_PREEXISTING_INSTALLATION', "1"); core.exportVariable('ALLOW_PREEXISTING_INSTALLATION', "1");
// TODO: retry due to all the things that go wrong // TODO: retry due to all the things that can go wrong
const nixInstall = await tc.downloadTool('https://nixos.org/nix/install'); const nixInstall = await tc.downloadTool('https://nixos.org/nix/install');
await exec.exec("sh", [nixInstall, "--daemon"]); await exec.exec("sh", [nixInstall, "--daemon"]);
@ -54,15 +48,24 @@ async function run() {
core.exportVariable('NIX_SSL_CERT_FILE', '/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt'); core.exportVariable('NIX_SSL_CERT_FILE', '/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt');
// TODO: nc doesn't work correctly on macOS :( // TODO: nc doesn't work correctly on macOS :(
//await exec.exec("sh", ["-c", "while ! nc -zU /nix/var/nix/daemon-socket/socket; do sleep 0.5; done"]); await awaitSocket();
// macOS needs time to reload the daemon :(
await exec.exec("sleep", ["10"]);
} }
} catch (error) { } catch (error) {
core.setFailed(`Action failed with error: ${error}`); core.setFailed(`Action failed with error: ${error}`);
throw(error); throw(error);
} }
} }
async function awaitSocket() {
const daemonSocket = createConnection({ path: '/nix/var/nix/daemon-socket/socket' });
daemonSocket.on('error', async () => {
console.log('Waiting for daemon socket to be available, reconnecting...');
await new Promise(resolve => setTimeout(resolve, 500));
await awaitSocket();
});
daemonSocket.on('connect', () => {
exit(0);
});
}
run(); run();