Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote activation #54

Merged
merged 42 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d8e1af5
wip
srid Jun 11, 2024
939ab74
disable by default
srid Jun 11, 2024
74356c8
deploy: use extra-experimental-features
srid Jun 12, 2024
c8c6093
umm
srid Jun 12, 2024
dd6c60e
rm activate option
srid Jun 12, 2024
02c0ad4
rm the apps indirection
srid Jun 12, 2024
3c1dd73
add overrideInputs
srid Jun 12, 2024
0804ac1
fix
srid Jun 12, 2024
ed40d14
fix again
srid Jun 12, 2024
6747d76
adapt
srid Jun 12, 2024
6300eda
further
srid Jun 12, 2024
10db1a1
Merge branch 'master' into deploy
srid Jun 18, 2024
8075952
Outdated comment
srid Jun 25, 2024
a314aa3
v2 activate (#56)
srid Jun 26, 2024
b56b120
Merge branch 'master' into deploy
srid Jun 26, 2024
696922e
refactor
srid Jun 26, 2024
7b1ed28
data as separate file
srid Jun 26, 2024
8b16d88
shift most other params to 'data'
srid Jun 26, 2024
e0920fa
put nu in one place
srid Jun 26, 2024
a427529
mv module to file
srid Jun 26, 2024
167b86e
activate.nix
srid Jun 26, 2024
5381d2f
diff
srid Jun 26, 2024
0bb9125
group
srid Jun 26, 2024
df7e1a1
flip order
srid Jun 26, 2024
a02dc6e
combine
srid Jun 26, 2024
cdbf865
ensure darwin-rebuild is in PATH
srid Jun 26, 2024
13bc520
..
srid Jun 26, 2024
e145f62
redundant
srid Jun 26, 2024
cdaa4f4
more redund
srid Jun 26, 2024
c1748b3
more
srid Jun 26, 2024
d76985e
+1
srid Jun 26, 2024
b7a68e5
moar
srid Jun 26, 2024
653a106
co
srid Jun 26, 2024
6c4cfe7
c
srid Jun 26, 2024
c0f2371
cc
srid Jun 26, 2024
741cf02
f
srid Jun 26, 2024
480bd5c
no warn
srid Jun 26, 2024
9b28535
mis
srid Jun 26, 2024
39ca3fd
docs
srid Jun 26, 2024
fa5aefb
d
srid Jun 26, 2024
ce94155
no go
srid Jun 26, 2024
4ed77c9
error handling
srid Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

# nixos-flake

A [flake-parts](https://flake.parts/) module to unify [NixOS](https://nixos.org/manual/nixos/stable/) + [nix-darwin](https://github.com/LnL7/nix-darwin) + [home-manager] configuration in a single flake, while providing a consistent interface (and enabling common modules) for both Linux and macOS.
A [flake-parts](https://flake.parts/) module to unify [NixOS](https://nixos.org/manual/nixos/stable/) + [nix-darwin](https://github.com/LnL7/nix-darwin) + [home-manager] configuration in a single flake, while providing a consistent interface (and enabling common modules) for both Linux and macOS. For motivation, see https://community.flake.parts/nixos-flake

Why? [See here](https://github.com/srid/nixos-flake/discussions/40#discussioncomment-7542918)

[home-manager]: https://github.com/nix-community/home-manager



## Getting Started

https://community.flake.parts/nixos-flake/start

## Examples

https://community.flake.parts/nixos-flake/examples

## Discussion

To discuss the project, post in [our Zulip](https://nixos.zulipchat.com/#narrow/stream/413948-nixos) (preferred) or in [Github Discussions](https://github.com/srid/nixos-flake/discussions).
70 changes: 70 additions & 0 deletions activate/activate.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std log
use nixos-flake.nu getData # This module is generated in Nix

let CURRENT_HOSTNAME = (hostname | str trim)

# Activate system configuration of local machine
#
# To activate a remote machine, use run with subcommands: `host <hostname>`
def main [] {
main host ($CURRENT_HOSTNAME)
}

# Activate system configuration of the given host
def 'main host' [
host: string # Hostname to activate (must match flake.nix name)
] {
let data = getData
if $host not-in $data.nixos-flake-configs {
log error $"Host '($host)' not found in flake. Available hosts=($data.nixos-flake-configs | columns)"
exit 1
}
let hostData = $data.nixos-flake-configs
| get $host
| insert "flake" $"($data.cleanFlake)#($host)"

log info $"(ansi grey)currentSystem=($data.system) currentHost=(ansi green_bold)($CURRENT_HOSTNAME)(ansi grey) targetHost=(ansi green_reverse)($host)(ansi reset)(ansi grey) hostData=($hostData)(ansi reset)"

let runtime = {
local: ($CURRENT_HOSTNAME == $host)
darwin: ($hostData.outputs.system in ["aarch64-darwin" "x86_64-darwin"])
}

if $runtime.local {
# Since the user asked to activate current host, do so.
log info $"Activating (ansi purple)locally(ansi reset)"
if $runtime.darwin {
log info $"(ansi blue_bold)>>>(ansi reset) darwin-rebuild switch --flake ($hostData.flake) ($hostData.outputs.nixArgs | str join)"
darwin-rebuild switch --flake $hostData.flake ...$hostData.outputs.nixArgs
} else {
log info $"(ansi blue_bold)>>>(ansi reset) nixos-rebuild switch --flake ($hostData.flake) ($hostData.outputs.nixArgs | str join) --use-remote-sudo "
nixos-rebuild switch --flake $hostData.flake ...$hostData.outputs.nixArgs --use-remote-sudo
}
} else {
# Remote activation request, so copy the flake and the necessary inputs
# and then activate over SSH.
if $hostData.sshTarget == null {
log error $"sshTarget not found in host data for ($host). Add `nixos-flake.sshTarget = \"user@hostname\";` to your configuration."
exit 1
}
log info $"Activating (ansi purple_reverse)remotely(ansi reset) on ($hostData.sshTarget)"
nix copy ($data.cleanFlake) --to ($"ssh-ng://($hostData.sshTarget)")

$hostData.outputs.overrideInputs | transpose key value | each { |input|
log info $"Copying input ($input.key) to ($hostData.sshTarget)"
log info $"(ansi blue_bold)>>>(ansi reset) nix copy ($input.value) --to ($"ssh-ng://($hostData.sshTarget)")"
nix copy ($input.value) --to ($"ssh-ng://($hostData.sshTarget)")
}

# We re-run this script, but on the remote host.
log info $'(ansi blue_bold)>>>(ansi reset) ssh -t ($hostData.sshTarget) nix --extra-experimental-features '"nix-command flakes"' run ($hostData.outputs.nixArgs | str join) $"($data.cleanFlake)#activate" host ($host)'
ssh -t $hostData.sshTarget nix --extra-experimental-features '"nix-command flakes"' run ...$hostData.outputs.nixArgs $"($data.cleanFlake)#activate host ($host)"
}
}

# TODO: Implement this, resolving https://github.com/srid/nixos-flake/issues/18
def 'main home' [] {
log error "Home activation not yet supported; use .#activate-home instead"
exit 1
}

61 changes: 61 additions & 0 deletions activate/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{ self, inputs', pkgs, lib, system, ... }:

let
# Workaround https://github.com/NixOS/nix/issues/8752
cleanFlake = lib.cleanSourceWith {
name = "nixos-flake-activate-flake";
src = self;
};
nixos-flake-configs = lib.mapAttrs (name: value: value.config.nixos-flake) (self.nixosConfigurations or { } // self.darwinConfigurations or { });
data = {
nixos-flake-configs = nixos-flake-configs;
system = system;
cleanFlake = cleanFlake;
};
dataFile = pkgs.writeTextFile {
name = "nixos-flake-activate-data";
text = ''
${builtins.toJSON data}
'';
};
nixosFlakeNuModule = pkgs.writeTextFile {
name = "nixos-flake.nu";
text = ''
export def getData [] {
open ${dataFile} | from json
}
'';
};
runtimeInputs =
# TODO: better way to check for nix-darwin availability
if pkgs.stdenv.isDarwin && lib.hasAttr "nix-darwin" inputs' then [
inputs'.nix-darwin.packages.default # Provides darwin-rebuild
] else [
pkgs.nixos-rebuild
];
nixNuModule = pkgs.writeTextFile {
name = "nix.nu";
text = ''
use std *
let bins = '${builtins.toJSON (builtins.map (p: "${p}/bin") runtimeInputs)}' | from json
if $bins != [] {
log debug $"Adding runtime inputs to PATH: ($bins)"
path add ...$bins
}
'';
};
nuPackage = pkgs.runCommandNoCC "nushell"
{
meta.mainProgram = "activate.nu";
} ''
mkdir -p $out/bin
cd $out/bin
echo "#!${pkgs.nushell}/bin/nu" >> activate.nu
cat ${nixNuModule} >> activate.nu
# echo 'print $"PATH = ($env.PATH)"' >> activate.nu
cat ${./activate.nu} >> activate.nu
chmod a+x activate.nu
cp ${nixosFlakeNuModule} nixos-flake.nu
'';
in
nuPackage
46 changes: 46 additions & 0 deletions doc/activate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

# Activation

`nixos-flake` provides the `.#activate` flake app that can be used in place of `nixos-rebuild` (if using NixOS) and `darwin-rebuild` (if using `nix-darwin`).

In addition, it can also activate the system over SSH -- see next section.

```sh
nix run .#activate
```

Usually, you'd make this your default package, so as to be able to use `nix run`. In `flake.nix`:

```nix
# In perSystem
{
packages.default = self'.packages.activate
}
```


{#remote}
## Remote Activation

You can use `nixos-flake` as a lightweight alternative to the various deployment tools such as `deploy-rs` and `colmena`. The `.#activate` app takes the hostname as an argument. If you set the `nixos-flake.sshTarget` option in your NixOS or nix-darwin configuration, it will run activation over the SSH connection.

Add the following to your configuration -- `nixosConfigurations.myhost` or `darwinConfigurations.myhost` (depending on the platform):

```nix
{
nixos-flake.sshTarget = "myuser@myhost";
}
```

Then, you will be able to run the following to deploy to `myhost` from any machine:

```sh
nix run .#activate host myhost
```

### Non-goals

Remote activation doesn't seek to replace other deployment tools, and as such doesn't provide features like rollbacks. It is meant for simple deployment use cases.

>[!NOTE] Future
> It is possible however that `nixos-flake` can grow to support more sophisticated deployment capabilities
1 change: 1 addition & 0 deletions doc/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
# Guide

- [[templates]]#
- [[activate]]#
- [[module-outputs]]#
14 changes: 14 additions & 0 deletions doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@ short-title: nixos-flake ❄️
template:
sidebar:
collapsed: true
emanote:
folder-folgezettel: false
---

# Managing OS and home configurations using `nixos-flake`

[nixos-flake](https://github.com/srid/nixos-flake) a [flake-parts](https://flake.parts/) module to unify [NixOS](https://nixos.org/manual/nixos/stable/) + [nix-darwin](https://github.com/LnL7/nix-darwin) + [home-manager] configuration in a single flake, while providing a consistent interface (and enabling common modules) for both Linux and macOS.

## Why?

nixos-flake provides the following features:

- [[activate]]: An `.#activate` flake app that works both on macOS and NixOS.
- `.#activate` can also *remotely* activate machines (be it macOS or NixOS) over SSH.
- All NixOS/ nix-darwin/ home-manager modules receive `specialArgs` which includes all the information in the top-level flake.
- This enables those modules to be aware of the flake inputs, for instance.
- A `.#update` flake app to update the primary inputs (which can be overriden)

## Getting Started

See: [[start]]# and [[guide]]#. For examples, see [[examples]]#

[home-manager]: https://github.com/nix-community/home-manager
Expand Down
17 changes: 10 additions & 7 deletions doc/module-outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ Importing the `nixos-flake` flake-parts module will autowire the following flake

| Name | Description |
| ---------------------------- | ---------------------------------------------- |
| `nixos-flake.lib` | Functions `mkLinuxSystem`, `mkMacosSystem` and `mkHomeConfiguration` |
| `nixosModules.home-manager` | Home-manager setup module for NixOS |
| `darwinModules_.home-manager` | Home-manager setup module for Darwin |
| `packages.update` | Flake app to update key flake inputs |
| `packages.activate` | Flake app to build & activate the system |
| `packages.activate-home` | Flake app to build & activate the `homeConfigurations` for current user |
| **nixos-flake.lib** | Functions `mkLinuxSystem`, `mkMacosSystem` and `mkHomeConfiguration` |
| **nixosModules.home-manager** | Home-manager setup module for NixOS |
| **darwinModules_.home-manager**[^und] | Home-manager setup module for Darwin |
| **packages.update** | Flake app to update key flake inputs |
| [[activate\|packages.activate]] | Flake app to build & activate the system (locally or remotely over SSH) |
| **packages.activate-home**[^home] | Flake app to build & activate the `homeConfigurations` for current user |

In addition, all of your NixOS/nix-darwin/home-manager modules implicitly receive the following `specialArgs`:

- `flake@{self, inputs, config}` (`config` is from flake-parts')
- `rosettaPkgs` (if on darwin)

**NOTE**: The module API is open to change. [All feedback welcome](https://github.com/srid/nixos-flake/issues/new).

[^home]: This will soon be removed in favour of the [[activate|activate app]]. See https://github.com/srid/nixos-flake/issues/18

[^und]: Why the underscore in `darwinModules_`? This is to workaround a segfault with flake-parts. See https://github.com/srid/nixos-config/issues/31
53 changes: 15 additions & 38 deletions flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ let
in
{
options = {
perSystem = mkPerSystemOption
({ config, self', inputs', pkgs, system, ... }: {
perSystem =
mkPerSystemOption ({ config, self', inputs', pkgs, system, ... }: {
options.nixos-flake = lib.mkOption {
default = { };
type = types.submodule {
Expand Down Expand Up @@ -47,40 +47,8 @@ in
'';
};

activate =
if hasNonEmptyAttr [ "darwinConfigurations" ] self || hasNonEmptyAttr [ "nixosConfigurations" ] self
then
pkgs.writeShellApplication
{
name = "activate";
text =
# TODO: Replace with deploy-rs or (new) nixinate
if system == "aarch64-darwin" || system == "x86_64-darwin" then
let
# This is used just to pull out the `darwin-rebuild` script.
# See also: https://github.com/LnL7/nix-darwin/issues/613
emptyConfiguration = self.nixos-flake.lib.mkMacosSystem { nixpkgs.hostPlatform = system; };
in
''
HOSTNAME=$(hostname -s)
set -x
${emptyConfiguration.system}/sw/bin/darwin-rebuild \
switch \
--flake "path:${self}#''${HOSTNAME}" \
"$@"
''
else
''
HOSTNAME=$(hostname -s)
set -x
${lib.getExe pkgs.nixos-rebuild} \
switch \
--flake "path:${self}#''${HOSTNAME}" \
--use-remote-sudo \
"$@"
'';
}
else null;
# New-style activate app that can also activately remotely over SSH.
activate = import ./activate { inherit self inputs' pkgs lib system; };

activate-home =
if hasNonEmptyAttr [ "homeConfigurations" ] self || hasNonEmptyAttr [ "legacyPackages" system "homeConfigurations" ] self
Expand All @@ -104,6 +72,7 @@ in

config = {
flake = {
nixosModules.nixosFlake = ./nix/nixos-module.nix;
# Linux home-manager module
nixosModules.home-manager = {
imports = [
Expand All @@ -115,6 +84,8 @@ in
})
];
};

darwinModules_.nixosFlake = ./nix/nixos-module.nix;
# macOS home-manager module
# This is named with an underscope, because flake-parts segfaults otherwise!
# See https://github.com/srid/nixos-config/issues/31
Expand All @@ -134,12 +105,18 @@ in
mkLinuxSystem = mod: inputs.nixpkgs.lib.nixosSystem {
# Arguments to pass to all modules.
specialArgs = specialArgsFor.nixos;
modules = [ mod ];
modules = [
self.nixosModules.nixosFlake
mod
];
};

mkMacosSystem = mod: inputs.nix-darwin.lib.darwinSystem {
specialArgs = specialArgsFor.darwin;
modules = [ mod ];
modules = [
self.darwinModules_.nixosFlake
mod
];
};

mkHomeConfiguration = pkgs: mod: inputs.home-manager.lib.homeManagerConfiguration {
Expand Down
Loading