nixinate/flake.nix
matthewcroughan aa9b423354 Add locking via flock(1)
This adds basic advisory locking such that two Nixinate deployments do
not run the activation script at the same time, both for local and
remote. The default timeout is 60 seconds, and is currently
unconfigurable.
2022-08-19 05:45:57 +01:00

109 lines
5.1 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
description = "Nixinate your systems 🕶";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, ... }@inputs:
let
version = builtins.substring 0 8 self.lastModifiedDate;
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
forSystems = systems: f:
nixpkgs.lib.genAttrs systems
(system: f system nixpkgs.legacyPackages.${system});
forAllSystems = forSystems supportedSystems;
nixpkgsFor = forAllSystems (system: pkgs: import nixpkgs { inherit system; overlays = [ self.overlay ]; });
in rec
{
herculesCI.ciSystems = [ "x86_64-linux" ];
overlay = final: prev: {
nixinate = {
nix = prev.pkgs.writeShellScriptBin "nix"
''${final.nixVersions.unstable}/bin/nix --experimental-features "nix-command flakes" "$@"'';
nixos-rebuild = prev.nixos-rebuild.override { inherit (final) nix; };
};
generateApps = flake:
let
machines = builtins.attrNames flake.nixosConfigurations;
validMachines = final.lib.remove "" (final.lib.forEach machines (x: final.lib.optionalString (flake.nixosConfigurations."${x}"._module.args ? nixinate) "${x}" ));
mkDeployScript = { machine, dryRun }: let
inherit (builtins) abort;
inherit (final.lib) getExe optionalString;
nix = "${getExe final.nix}";
nixos-rebuild = "${getExe final.nixos-rebuild}";
openssh = "${getExe final.openssh}";
n = flake.nixosConfigurations.${machine}._module.args.nixinate;
hermetic = n.hermetic or false;
user = n.sshUser or "root";
host = n.host;
where = n.buildOn or "remote";
remote = if where == "remote" then true else if where == "local" then false else abort "_module.args.nixinate.buildOn is not set to a valid value of 'local' or 'remote'";
substituteOnTarget = n.substituteOnTarget or false;
switch = if dryRun then "dry-activate" else "switch";
script =
''
set -e
echo "🚀 Deploying nixosConfigurations.${machine} from ${flake}"
echo "👤 SSH User: ${user}"
echo "🌐 SSH Host: ${host}"
'' + (if remote then ''
echo "🚀 Sending flake to ${machine} via nix copy:"
( set -x; ${nix} copy ${flake} --to ssh://${user}@${host} )
'' + (if hermetic then ''
echo "🤞 Activating configuration hermetically on ${machine} via ssh:"
( set -x; ${nix} copy --derivation ${nixos-rebuild} --to ssh://${user}@${host} )
( set -x; ${openssh} -t ${user}@${host} "sudo flock -w 60 /dev/shm/nixinate-${machine} -c 'nix-store --realise ${nixos-rebuild} && sudo ${nixos-rebuild} ${switch} --flake ${flake}#${machine}'" )
'' else ''
echo "🤞 Activating configuration non-hermetically on ${machine} via ssh:"
( set -x; ${openssh} -t ${user}@${host} "sudo flock -w 60 /dev/shm/nixinate-${machine} -c 'nixos-rebuild ${switch} --flake ${flake}#${machine}'" )
'')
else ''
echo "🔨 Building system closure locally, copying it to remote store and activating it:"
( set -x; NIX_SSHOPTS="-t" flock -w 60 /dev/shm/nixinate-${machine} -c '${nixos-rebuild} ${switch} --flake ${flake}#${machine} --target-host ${user}@${host} --use-remote-sudo ${optionalString substituteOnTarget "-s"}' )
'');
in final.writeScript "deploy-${machine}.sh" script;
in
{
nixinate =
(
nixpkgs.lib.genAttrs
validMachines
(x:
{
type = "app";
program = toString (mkDeployScript {
machine = x;
dryRun = false;
});
}
)
// nixpkgs.lib.genAttrs
(map (a: a + "-dry-run") validMachines)
(x:
{
type = "app";
program = toString (mkDeployScript {
machine = nixpkgs.lib.removeSuffix "-dry-run" x;
dryRun = true;
});
}
)
);
};
};
nixinate = forAllSystems (system: pkgs: nixpkgsFor.${system}.generateApps);
checks = forAllSystems (system: pkgs:
let
vmTests = import ./tests {
makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest;
inherit inputs; pkgs = nixpkgsFor.${system};
};
in
pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux.
//
{
# Other checks here...
}
);
};
}