diff --git a/flake.lock b/flake.lock index d2f121414..839994d62 100644 --- a/flake.lock +++ b/flake.lock @@ -131,6 +131,26 @@ "type": "github" } }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767718503, + "narHash": "sha256-V+VkFs0aSG0ca8p/N3gib7FAf4cq9jyr5Gm+ZBrHQpo=", + "owner": "nix-darwin", + "repo": "nix-darwin", + "rev": "9f48ffaca1f44b3e590976b4da8666a9e86e6eb1", + "type": "github" + }, + "original": { + "owner": "nix-darwin", + "repo": "nix-darwin", + "type": "github" + } + }, "nix-editor": { "inputs": { "nixpkgs": [ @@ -262,6 +282,7 @@ "flake-parts": "flake-parts", "flake-utils": "flake-utils", "git-hooks": "git-hooks", + "nix-darwin": "nix-darwin", "nix-editor": "nix-editor", "nix-eval-jobs": "nix-eval-jobs", "nix2container": "nix2container", diff --git a/flake.nix b/flake.nix index 2145185d1..4d9ccdbe4 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,8 @@ flake-utils.url = "github:numtide/flake-utils"; git-hooks.inputs.nixpkgs.follows = "nixpkgs"; git-hooks.url = "github:cachix/git-hooks.nix"; + nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; + nix-darwin.url = "github:nix-darwin/nix-darwin"; nix-editor.inputs.nixpkgs.follows = "nixpkgs"; nix-editor.inputs.utils.follows = "flake-utils"; nix-editor.url = "github:snowfallorg/nix-editor"; @@ -47,6 +49,7 @@ nix/devShells.nix nix/fmt.nix nix/hooks.nix + nix/hosts.nix nix/nixpkgs.nix nix/packages nix/overlays diff --git a/nix/docs/nixos-tests-on-macos.md b/nix/docs/nixos-tests-on-macos.md new file mode 100644 index 000000000..ae43b4f7d --- /dev/null +++ b/nix/docs/nixos-tests-on-macos.md @@ -0,0 +1,189 @@ +## Prerequisites + +Running NixOS tests on macOS requires a Linux builder VM because NixOS tests need a Linux environment. +This project includes a nix-darwin configuration that sets up a linux-builder VM automatically. + +You need: +- macOS with Apple Silicon (aarch64-darwin) +- Nix installed (see [Getting Started](start-here.md)) preferably a recent version (2.30+) + +## Setup + +Run the setup script to configure nix-darwin with the linux-builder: + +```bash +nix run .#setup-darwin-linux-builder +``` + +Note that you don't have to checkout the repository to run the setup, you can run it directly from GitHub: + +```bash +nix run github:supabase/postgres#setup-darwin-linux-builder +``` + +This command will: +- Back up existing system files (`/etc/nix/nix.conf`, `/etc/bashrc`, `/etc/zshrc`) +- Configure nix-darwin with the linux-builder VM +- Install helper scripts for managing the builder + +The linux-builder VM is configured with: +- 6 CPU cores +- 8GB RAM +- 40GB disk +- Support for both x86_64-linux and aarch64-linux builds +- The `nixos-test` feature required for running NixOS tests + +After setup completes, restart your shell to access the helper commands. + +## Verify the setup + +The setup script runs verification automatically after configuration. +You can also run verification manually at any time: + +```bash +nix run .#verify-darwin-linux-builder +``` + +Or after setup, use the installed command: + +```bash +verify-darwin-linux-builder +``` + +The verification script checks: + +1. Launchd service status (running vs loaded-but-stopped) +2. Nix configuration via `nix config show` (substituters, trusted keys, experimental features) +3. Builder features (`/etc/nix/machines` includes `nixos-test`) +4. Builder responsiveness (test build of `nixpkgs#hello` for aarch64-linux) + +Each check reports pass/fail with actionable guidance on failures. + +You can also manually test that the linux-builder is working by building a simple package for Linux: + +```bash +nix build --system x86_64-linux nixpkgs#hello +nix build --system aarch64-linux nixpkgs#hello +``` + +If both commands succeed, the linux-builder is ready for NixOS tests. + +## Running NixOS tests + +NixOS tests are defined in `nix/ext/tests/` and exposed as flake checks. +To run a test on macOS, use the `aarch64-darwin` system attribute: + +```bash +nix build .#checks.aarch64-darwin.ext-pgjwt -L +``` + +The `-L` flag shows logs during the build, which is helpful for seeing test progress and debugging failures. + +If the nix build exit immediately with success, it means that the result was fetched from cache and the test passed previously. +To force a re-run of the test, use the `--rebuild` flag: + +```bash +nix build .#checks.aarch64-darwin.ext-pgjwt -L --rebuild +``` + +### Available tests + +List all available checks with: + +```bash +nix flake show --json 2>/dev/null | jq -r '.checks["aarch64-darwin"] | keys[]' | sort +``` + +Extension tests follow the naming pattern `ext-`: + +```bash +nix build .#checks.aarch64-darwin.ext-pgjwt -L +nix build .#checks.aarch64-darwin.ext-postgis -L +nix build .#checks.aarch64-darwin.ext-vector -L +nix build .#checks.aarch64-darwin.ext-pg_graphql -L +``` + +## Managing the linux-builder VM + +The setup installs two helper commands for controlling the VM: + +```bash +stop-linux-builder # Stop the VM (pauses resource usage) +start-linux-builder # Start the VM again +``` + +As the VM can consume significant resources, you may want to stop it when not running tests using `stop-linux-builder`. +When stopped with `stop-linux-builder`, the service is unloaded to prevent automatic restart. +Use `start-linux-builder` to re-enable and start the service. + +### Checking VM status + +```bash +sudo launchctl list | grep linux-builder +``` + +If the VM is running, you'll see a line containing `org.nixos.linux-builder`. + +## Troubleshooting + +### Tests fail with "builder not available" + +Ensure the linux-builder is running: + +```bash +start-linux-builder +``` + +Then verify with a simple build: + +```bash +nix build --system aarch64-linux nixpkgs#hello +``` + +### VM won't start after reboot + +If the VM doesn't start automatically, run: + +```bash +start-linux-builder +``` + +The VM is configured as ephemeral, meaning it's recreated fresh on each start. +This ensures a clean environment but requires re-downloading cached build artifacts. + +### Slow first build + +The first NixOS test run may download significant data. +Subsequent runs benefit from the Nix store cache and the project's binary cache at `nix-postgres-artifacts.s3.amazonaws.com`. + +## How it works + +The linux-builder is a QEMU virtual machine managed by nix-darwin. +When you run a build targeting Linux (like NixOS tests), Nix automatically delegates the build to this VM. + +Key configuration from `nix/hosts/darwin-nixostest/darwin-configuration.nix`: + +```nix +nix.linux-builder = { + enable = true; + ephemeral = true; + maxJobs = 4; + supportedFeatures = [ + "kvm" + "benchmark" + "big-parallel" + "nixos-test" # Required for NixOS integration tests + ]; + config = { + virtualisation = { + darwin-builder = { + diskSize = 40 * 1024; # 40GB + memorySize = 8 * 1024; # 8GB + }; + cores = 6; + }; + }; +}; +``` + +The `nixos-test` supported feature is what enables running NixOS VM tests from macOS. diff --git a/nix/hosts.nix b/nix/hosts.nix new file mode 100644 index 000000000..f8f7c394d --- /dev/null +++ b/nix/hosts.nix @@ -0,0 +1,11 @@ +{ inputs, self, ... }: +{ + flake = { + darwinConfigurations = { + darwin-nixostest = inputs.nix-darwin.lib.darwinSystem { + specialArgs = { inherit self; }; + modules = [ ./hosts/darwin-nixostest/darwin-configuration.nix ]; + }; + }; + }; +} diff --git a/nix/hosts/darwin-nixostest/darwin-configuration.nix b/nix/hosts/darwin-nixostest/darwin-configuration.nix new file mode 100644 index 000000000..210880b96 --- /dev/null +++ b/nix/hosts/darwin-nixostest/darwin-configuration.nix @@ -0,0 +1,135 @@ +{ + lib, + pkgs, + self, + ... +}: +let + start-linux-builder = pkgs.writeShellApplication { + name = "start-linux-builder"; + text = '' + echo "Starting linux-builder..." + + if sudo launchctl list | grep -q org.nixos.linux-builder; then + echo "linux-builder is already running" + exit 0 + fi + + # Use load instead of start to re-enable the service + if sudo launchctl load -w /Library/LaunchDaemons/org.nixos.linux-builder.plist 2>/dev/null; then + echo "linux-builder started successfully" + else + echo "Error: Could not start linux-builder" + echo "Make sure nix-darwin is configured with linux-builder enabled" + exit 1 + fi + + # Check if it's running + sleep 2 + if sudo launchctl list | grep -q org.nixos.linux-builder; then + echo "linux-builder is now running" + else + echo "Warning: linux-builder may not have started properly" + fi + ''; + }; + stop-linux-builder = pkgs.writeShellApplication { + name = "stop-linux-builder"; + text = '' + echo "Stopping linux-builder..." + + # Use unload instead of stop because KeepAlive=true will restart it + if sudo launchctl unload -w /Library/LaunchDaemons/org.nixos.linux-builder.plist 2>/dev/null; then + echo "linux-builder stopped successfully" + else + echo "Warning: Could not stop linux-builder (it may not be running)" + fi + + # Check if it's still running + sleep 1 + if sudo launchctl list | grep -q org.nixos.linux-builder; then + echo "Warning: linux-builder is still running" + STATUS=$(sudo launchctl list | grep org.nixos.linux-builder || true) + echo "Current status: $STATUS" + else + echo "linux-builder is not running" + fi + ''; + }; + verify-darwin-linux-builder = self.packages.aarch64-darwin.verify-darwin-linux-builder; +in +{ + nixpkgs.hostPlatform = "aarch64-darwin"; + + # Install builder control scripts + environment.systemPackages = [ + start-linux-builder + stop-linux-builder + verify-darwin-linux-builder + ]; + + nix.settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + always-allow-substitutes = true; + max-jobs = "auto"; + trusted-users = [ "@admin" ]; + extra-substituters = [ "https://nix-postgres-artifacts.s3.amazonaws.com" ]; + extra-trusted-substituters = [ "https://nix-postgres-artifacts.s3.amazonaws.com" ]; + extra-trusted-public-keys = [ + "nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI=" + ]; + }; + + nix.extraOptions = '' + !include nix.custom.conf + ''; + + # accept existing nix.custom.conf + system.activationScripts.checks.text = lib.mkForce ""; + system.activationScripts.nix-daemon.text = lib.mkForce '' + if ! diff /etc/nix/nix.conf /run/current-system/etc/nix/nix.conf &> /dev/null || ! diff /etc/nix/machines /run/current-system/etc/nix/machines &> /dev/null; then + echo "reloading nix-daemon..." >&2 + launchctl kill HUP system/org.nixos.nix-daemon + fi + max_wait=30 + waited=0 + while ! nix-store --store daemon -q --hash ${pkgs.stdenv.shell} &>/dev/null; do + if [ $waited -ge $max_wait ]; then + echo "ERROR: nix-daemon failed to start after $max_wait seconds" >&2 + exit 1 + fi + echo "waiting for nix-daemon" >&2 + launchctl kickstart system/org.nixos.nix-daemon + sleep 1 + waited=$((waited + 1)) + done + ''; + + nix.linux-builder = { + enable = true; + ephemeral = true; + maxJobs = 4; + supportedFeatures = [ + "kvm" + "benchmark" + "big-parallel" + "nixos-test" + ]; + config = { + virtualisation = { + darwin-builder = { + diskSize = 40 * 1024; + memorySize = 8 * 1024; + }; + cores = 6; + }; + }; + }; + + nix.distributedBuilds = true; + + system.stateVersion = 6; +} diff --git a/nix/mkdocs.yml b/nix/mkdocs.yml index a6e2bc7ad..bf7c4752d 100644 --- a/nix/mkdocs.yml +++ b/nix/mkdocs.yml @@ -31,6 +31,7 @@ nav: - Adding Tests: adding-tests.md - Migration Tests: migration-tests.md - Testing PG Upgrade Scripts: testing-pg-upgrade-scripts.md + - NixOS Tests on macOS: nixos-tests-on-macos.md - References: references.md validation: diff --git a/nix/packages/default.nix b/nix/packages/default.nix index 2c63f2223..224684a65 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -86,6 +86,12 @@ cargo-pgrx_0_14_3 ; } + // lib.optionalAttrs pkgs.stdenv.isDarwin { + setup-darwin-linux-builder = pkgs.callPackage ./setup-darwin-linux-builder.nix { + inherit inputs self; + }; + verify-darwin-linux-builder = pkgs.callPackage ./verify-darwin-linux-builder.nix { }; + } // lib.filterAttrs (n: _v: n != "override" && n != "overrideAttrs" && n != "overrideDerivation") ( pkgs.callPackage ../postgresql/default.nix { inherit self'; diff --git a/nix/packages/setup-darwin-linux-builder.nix b/nix/packages/setup-darwin-linux-builder.nix new file mode 100644 index 000000000..58d1787fb --- /dev/null +++ b/nix/packages/setup-darwin-linux-builder.nix @@ -0,0 +1,75 @@ +{ + inputs, + self, + stdenv, + writeShellApplication, +}: +writeShellApplication { + name = "setup-darwin-linux-builder"; + runtimeInputs = [ + inputs.nix-darwin.packages.${stdenv.hostPlatform.system}.darwin-rebuild + ]; + text = '' + set -euo pipefail + + echo "Configuring nix-darwin linux-builder..." + echo "" + + # Backup files that nix-darwin will manage + echo "Preparing for nix-darwin..." + for file in /etc/nix/nix.conf /etc/bashrc /etc/zshrc; do + if [[ -f "$file" && ! -L "$file" ]]; then + echo " Backing up $file" + sudo mv "$file" "$file.before-nix-darwin" + fi + done + echo "" + + revert() { + for file in /etc/nix/nix.conf /etc/bashrc /etc/zshrc; do + if [[ ! -L "$file" && -f "$file.before-nix-darwin" ]]; then + echo " Restoring original $file" + sudo mv "$file.before-nix-darwin" "$file" + fi + done + } + trap revert ERR SIGINT SIGTERM + + echo "This will configure your system with:" + echo " - NixOS linux-builder VM (ephemeral)" + echo " - 6 cores, 8GB RAM, 40GB disk" + echo " - Support for x86_64-linux and aarch64-linux builds" + echo "" + echo "Running darwin-rebuild switch..." + echo "" + + sudo darwin-rebuild switch --refresh --flake ${self}#darwin-nixostest + + echo "" + echo "Configuration complete!" + echo "" + + echo "Running verification..." + echo "" + if nix run ${self}#verify-darwin-linux-builder; then + echo "" + echo "Setup and verification successful!" + else + echo "" + echo "Setup completed but verification found issues." + echo "Review the failures above and try:" + echo " nix run .#verify-darwin-linux-builder" + echo "" + echo "to re-run verification after addressing any issues." + exit 1 + fi + + echo "" + echo "To control the linux builder vm, you can use:" + echo " stop-linux-builder # stop the linux builder vm" + echo " start-linux-builder # start the linux builder vm" + echo " verify-darwin-linux-builder # verify the setup is working" + echo "" + echo "If this is the first install, you may need to restart your shell to use these scripts." + ''; +} diff --git a/nix/packages/verify-darwin-linux-builder.nix b/nix/packages/verify-darwin-linux-builder.nix new file mode 100644 index 000000000..fda6f1184 --- /dev/null +++ b/nix/packages/verify-darwin-linux-builder.nix @@ -0,0 +1,129 @@ +{ + coreutils, + gawk, + gnugrep, + writeShellApplication, +}: +writeShellApplication { + name = "verify-darwin-linux-builder"; + runtimeInputs = [ + coreutils + gawk + gnugrep + ]; + text = '' + set -euo pipefail + + PASS=0 + FAIL=0 + + check_pass() { + echo " [PASS] $1" + PASS=$((PASS + 1)) + } + + check_fail() { + echo " [FAIL] $1" + echo " $2" + FAIL=$((FAIL + 1)) + } + + echo "Verifying darwin-linux-builder configuration..." + echo "" + + echo "1. Checking launchd service status..." + if sudo launchctl list org.nixos.linux-builder &>/dev/null; then + SERVICE_OUTPUT=$(sudo launchctl list org.nixos.linux-builder 2>/dev/null || true) + PID=$(echo "$SERVICE_OUTPUT" | grep -E "^\s*\"PID\"" | grep -oE '[0-9]+' || echo "-") + if [[ "$PID" != "-" && -n "$PID" ]]; then + check_pass "linux-builder service is running (PID: $PID)" + else + check_fail "linux-builder service is loaded but not running" \ + "Run: start-linux-builder" + fi + else + check_fail "linux-builder service not found" \ + "Run: nix run .#setup-darwin-linux-builder" + fi + + echo "" + echo "2. Checking nix configuration..." + NIX_CONFIG=$(nix config show 2>/dev/null || true) + if [[ -n "$NIX_CONFIG" ]]; then + if echo "$NIX_CONFIG" | grep -q "nix-postgres-artifacts.s3.amazonaws.com"; then + check_pass "Substituter configured for nix-postgres-artifacts" + else + check_fail "Missing substituter for nix-postgres-artifacts" \ + "Expected: nix-postgres-artifacts.s3.amazonaws.com in substituters" + fi + + if echo "$NIX_CONFIG" | grep -q "nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI="; then + check_pass "Trusted public key configured" + else + check_fail "Missing trusted public key" \ + "Expected signing key for nix-postgres-artifacts in trusted-public-keys" + fi + + if echo "$NIX_CONFIG" | grep -qE "experimental-features.*nix-command" && \ + echo "$NIX_CONFIG" | grep -qE "experimental-features.*flakes"; then + check_pass "Experimental features enabled (nix-command, flakes)" + else + check_fail "Missing experimental features" \ + "Expected: nix-command and flakes in experimental-features" + fi + else + check_fail "Could not read nix configuration" \ + "Run: nix config show" + fi + + echo "" + echo "3. Checking builder features..." + MACHINES_FILE="/etc/nix/machines" + if [[ -f "$MACHINES_FILE" ]]; then + # machines file format: uri systems key maxjobs speedfactor features mandatory-features public-key + # Features are in field 6 (1-indexed), comma-separated + FEATURES=$(awk '{print $6}' "$MACHINES_FILE" | tr ',' ' ' || true) + if grep -q "nixos-test" "$MACHINES_FILE"; then + check_pass "nixos-test feature supported" + echo " Available features: $FEATURES" + else + check_fail "nixos-test feature not configured" \ + "Expected: nixos-test in $MACHINES_FILE" + fi + else + check_fail "machines file not found" \ + "Expected: $MACHINES_FILE" + fi + + echo "" + echo "4. Testing builder responsiveness..." + echo " Building nixpkgs#hello for aarch64-linux (timeout: 60s)..." + if timeout 60 nix build --system aarch64-linux nixpkgs#hello --no-link --print-out-paths 2>/dev/null; then + check_pass "Builder is responsive and can build aarch64-linux packages" + else + EXIT_CODE=$? + if [[ $EXIT_CODE -eq 124 ]]; then + check_fail "Builder timed out after 60 seconds" \ + "The builder may be unresponsive or overloaded. Try: stop-linux-builder && start-linux-builder" + else + check_fail "Builder failed to build test package" \ + "Check builder logs: sudo launchctl list org.nixos.linux-builder" + fi + fi + + echo "" + echo "========================================" + echo "Verification complete: $PASS passed, $FAIL failed" + echo "========================================" + + if [[ $FAIL -gt 0 ]]; then + echo "" + echo "Some checks failed. Review the failures above for guidance." + exit 1 + else + echo "" + echo "All checks passed! The darwin-linux-builder is ready for use." + exit 0 + fi + ''; +}