{pkgs, ...}: { name = "turtle-sync"; node = {}; nodes = let atuinSession = "01969ec6b8d07e30a9d2df0911fbfe2a"; in { acme = { imports = [ ./common/acme/server.nix ./common/dns/client.nix ../nix/module.nix ]; }; name_server = {nodes, ...}: { imports = [ ./common/acme/client.nix ./common/dns/server.nix ../nix/module.nix ]; vhack.dns.zones = { "turtle-sync.server" = { SOA = { nameServer = "ns"; adminEmail = "admin@server.com"; serial = 2025012301; }; useOrigin = false; A = [ nodes.server.networking.primaryIPAddress ]; AAAA = [ nodes.server.networking.primaryIPv6Address ]; }; }; }; server = {config, ...}: let turtleCfg = config.services.turtle; in { imports = [ ../nix/module.nix ./common/acme/client.nix ./common/dns/client.nix ]; config = { services = { postgresql.enable = true; turtle = { enable = true; host = "127.0.0.1"; database.createLocally = true; }; nginx = { enable = true; recommendedTlsSettings = true; recommendedOptimisation = true; recommendedGzipSettings = true; recommendedProxySettings = true; virtualHosts."turtle-sync.server" = { locations."/" = { proxyPass = "http://${turtleCfg.host}:${toString turtleCfg.port}"; recommendedProxySettings = true; proxyWebsockets = true; }; enableACME = true; forceSSL = true; }; }; }; networking.firewall = { allowedTCPPorts = [80 443]; }; }; }; client1 = { config, pkgs, ... }: { imports = [ ../nix/module.nix ./common/acme/client.nix ./common/dns/client.nix ]; environment.sessionVariables.ATUIN_SESSION = atuinSession; }; client2 = { config, pkgs, ... }: { imports = [ ../nix/module.nix ./common/acme/client.nix ./common/dns/client.nix ]; environment.sessionVariables.ATUIN_SESSION = atuinSession; }; }; testScript = {nodes, ...}: let mkSyncConfig = pkgs.writeShellScript "write-turtle-sync-config" '' mkdir --parents ~/.config/atuin/ cat << EOF > ~/.config/atuin/config.toml [sync] address = "https://turtle-sync.server" user_id_path = "${pkgs.writeText "user-id" "019eb88a-6b51-7e52-b12c-7d30bd8e5928"}" encryption_key_path = "${pkgs.writeText "encryption-key" "3AAgbWsDzL7M00/Mq0LMjsyOCy3MnsypBsyQzKbMywNGzNnMrUBozIINAxdbIiDMhQ=="}" EOF ''; runCommandAndRecordInTurtle = pkgs.writeShellScript "run-command-and-record-in-turtle" '' # SPDX-SnippetBegin # SPDX-SnippetCopyrightText: 2023 mentalisttraceur (https://github.com/mentalisttraceur) # Source: https://github.com/atuinsh/atuin/issues/1188#issuecomment-1698354107 run_and_record_in_turtle() { local id local status local escaped_command="$(printf '%q ' "$@")" id="$(atuin history start -- "$escaped_command")" "$@" status=$? atuin history end --exit $status "$id" return $status } # SPDX-SnippetEnd run_and_record_in_turtle "$@" ''; acme = import ./common/acme {inherit pkgs;}; in acme.prepare ["server" "client1" "client2"] # Python '' server.wait_for_unit("turtle.service") server.wait_for_open_port(443) # Wait for the server to acquire the acme certificate client1.wait_until_succeeds("curl https://turtle-sync.server") with subtest("Setup client syncing"): for client in [client1, client2]: client.succeed("${mkSyncConfig}") with subtest("Can generate shell history"): client1.succeed("${runCommandAndRecordInTurtle} echo hi - client 1") client2.succeed("${runCommandAndRecordInTurtle} echo hi - client 2") with subtest("Can sync"): for client in [client1, client2]: client.succeed("atuin sync perform --force") client1.succeed("atuin sync perform --force") with subtest("Have correct tasks"): hist1 = client1.succeed("atuin history list --format '{command}'").strip().split('\n') hist2 = client2.succeed("atuin history list --format '{command}'").strip().split('\n') hist1.sort() hist2.sort() canonicalHistory = [ "echo hi - client 1", "echo hi - client 2" ] assert hist1 == hist2, f"The clients don't have the same amount of history items, client1: '{hist1}', client2: '{hist2}'" assert hist1 == canonicalHistory, f"The history is not correct: '{hist1}' vs. '{canonicalHistory}'" ''; }