mirror of
https://github.com/cachix/install-nix-action.git
synced 2024-11-24 01:18:05 +00:00
commit
f57eb6b95e
3 changed files with 141 additions and 30 deletions
102
lib/create-darwin-volume.sh
Executable file
102
lib/create-darwin-volume.sh
Executable 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 "$@"
|
32
lib/main.js
32
lib/main.js
|
@ -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();
|
||||||
|
|
37
src/main.ts
37
src/main.ts
|
@ -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
|
||||||
|
@ -17,28 +20,19 @@ 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();
|
||||||
|
|
Loading…
Reference in a new issue