init-less install (#188)

* wip

* Add dockerfile

* Add readme bits

* Fix logic inversion

* Relax init detection error

* Tidying heuristics

* Fix doc tests

* Mac supports start-daemon false

* Get VM tests working

* Add instructions

* Some target_os flagging

* More target flagging

* Fix lints

* Fixup more mac-only stuff

* cfg flag examples too

* Fix planner wording

* More os specific lint fixing

* More refinement on mac and the README

* Add new CI jobs to test no-daemon

* Use nix-installer-pr to point at branch

* Tests with no-init

* init/no-daemon are linux only

* nix tests support a per-distro all attribute

* Add working podman test

* Expand docker tests

* Add contributing notes

* format

* Support both podman and docker

* Update contributing

* Add Windows WSL test script for Ubuntu

* format nix tests

* More ignores to check-spelling

* Add systemd based wsl test

* We don't have root-only darwin

* Reflect review nits

* Reenable tests

* Restore mac plan

* Flag off os specific tests

* Better cfg flagging

* Remove dead comments

* Rework readme to look better with new sections

* Correct codeblock language

* Remove some warnings
This commit is contained in:
Ana Hobden 2023-02-01 12:35:52 -08:00 committed by GitHub
parent 4d2e2c3911
commit d69f335703
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 2229 additions and 1529 deletions

1
.dockerignore Normal file
View file

@ -0,0 +1 @@
target

View file

@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Cache lint store (x86_64-linux)
@ -36,7 +36,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Cache build store (x86_64-linux)
@ -67,7 +67,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Cache build store (x86_64-linux)
@ -100,7 +100,7 @@ jobs:
cp nix-installer.sh install-root/nix-installer.sh
mv nix-installer install-root/nix-installer-x86_64-linux
- name: Initial install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -129,7 +129,7 @@ jobs:
exit 1
fi
- name: Repeated install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -184,6 +184,113 @@ jobs:
exit 1
fi
run-x86_64-linux-no-init:
name: Run x86_64 Linux (No init)
runs-on: ubuntu-22.04
needs: [build-x86_64-linux, lints]
steps:
- uses: actions/checkout@v3
- run: sudo apt install fish zsh
- uses: actions/download-artifact@v3
with:
name: nix-installer-x86_64-linux
- name: Move & set executable
run: |
chmod +x ./nix-installer
mkdir install-root
cp nix-installer.sh install-root/nix-installer.sh
mv nix-installer install-root/nix-installer-x86_64-linux
- name: Initial install
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
init: none
planner: linux-multi
local-root: install-root/
logger: pretty
log-directives: nix_installer=trace
backtrace: full
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure daemon was not configured with init
run: |
if systemctl is-active nix-daemon.socket; then
echo "nix-daemon.socket was running"
exit 1
fi
if systemctl is-active nix-daemon.service; then
echo "nix-daemon.service was running"
exit 1
fi
- name: Initial uninstall (without a `nix run` first)
run: sudo -E /nix/nix-installer uninstall
env:
NIX_INSTALLER_NO_CONFIRM: true
NIX_INSTALLER_LOGGER: pretty
NIX_INSTALLER_LOG_DIRECTIVES: nix_installer=trace
RUST_BACKTRACE: full
- name: Ensure `nix` is removed
run: |
if [ -e /nix ]; then
echo "/nix exists"
exit 1
fi
- name: Repeated install
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
init: none
planner: linux-multi
local-root: install-root/
logger: pretty
log-directives: nix_installer=trace
backtrace: full
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: echo $PATH
run: echo $PATH
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
sudo -i nix run nixpkgs#fortune
sudo -i nix profile install nixpkgs#fortune
fortune
sudo -i nix store gc
sudo -i nix run nixpkgs#fortune
- name: Test bash
run: sudo -i nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: bash --login {0}
- name: Test sh
run: sudo -i nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: sh -l {0}
- name: Test zsh
run: sudo -i nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: zsh --login --interactive {0}
- name: Test fish
run: sudo -i nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: fish --login {0}
- name: Repeated uninstall
run: sudo -E /nix/nix-installer uninstall
env:
NIX_INSTALLER_NO_CONFIRM: true
NIX_INSTALLER_LOGGER: pretty
NIX_INSTALLER_LOG_DIRECTIVES: nix_installer=trace
RUST_BACKTRACE: full
- name: Ensure `nix` is removed
run: |
if systemctl is-active nix-daemon.socket; then
echo "nix-daemon.socket was running"
exit 1
fi
if systemctl is-active nix-daemon.service; then
echo "nix-daemon.service was running"
exit 1
fi
if [ -e /nix ]; then
echo "/nix exists"
exit 1
fi
run-steam-deck:
name: Run Steam Deck (mock)
runs-on: ubuntu-22.04
@ -207,7 +314,7 @@ jobs:
sudo chmod +x /bin/steamos-readonly
sudo useradd -m deck
- name: Initial install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -242,7 +349,7 @@ jobs:
exit 1
fi
- name: Repeated install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -309,7 +416,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Runs clippy as part of the preBuild.
@ -339,7 +446,7 @@ jobs:
cp nix-installer.sh install-root/nix-installer.sh
mv nix-installer install-root/nix-installer-x86_64-darwin
- name: Initial install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -356,7 +463,7 @@ jobs:
NIX_INSTALLER_LOG_DIRECTIVES: nix_installer=trace
RUST_BACKTRACE: full
- name: Repeated install
uses: DeterminateSystems/nix-installer-action@main
uses: DeterminateSystems/nix-installer-action@start-daemon-and-init
with:
local-root: install-root/
logger: pretty
@ -397,4 +504,3 @@ jobs:
NIX_INSTALLER_LOGGER: pretty
NIX_INSTALLER_LOG_DIRECTIVES: nix_installer=trace
RUST_BACKTRACE: full

View file

@ -156,7 +156,7 @@ It's perfectly fine if they are manual or labor intensive, as these should be a
## `qemu` VM tests
In `nix/tests/vm-test` there exists some Nix derivations which we expose in the flake via `hydraJobs`.
For x86_64 Linux we have some additional QEMU based tests. In `nix/tests/vm-test` there exists some Nix derivations which we expose in the flake via `hydraJobs`.
These should be visible in `nix flake show`:
@ -190,13 +190,13 @@ git+file:///home/ana/git/determinatesystems/nix-installer
To run all of the currently supported tests:
```bash
nix build .#hydraJobs.vm-test.all.x86_64-linux.install-default -L
nix build .#hydraJobs.vm-test.all.x86_64-linux.all -L
```
To run a specific distribution listed in the `nix flake show` output:
```bash
nix build .#hydraJobs.vm-test.rhel-v7.x86_64-linux.install-default -L
nix build .#hydraJobs.vm-test.rhel-v7.x86_64-linux.all -L
```
For PR review, you can also test arbitrary branches or checkouts like so:
@ -238,6 +238,120 @@ installer-test-rhel-v7-install-default> Formatting './disk.qcow2', fmt=qcow2 clu
</details>
## Container tests
For x86_64 Linux we have some additional container tests. In `nix/tests/container-test` there exists some Nix derivations which we expose in the flake via `hydraJobs`.
These should be visible in `nix flake show`:
```
nix flake show
warning: Git tree '/home/ana/git/determinatesystems/nix-installer' is dirty
git+file:///home/ana/git/determinatesystems/nix-installer
# ...
├───hydraJobs
│ ├───container-test
│ │ ├───all
│ │ │ └───x86_64-linux
│ │ │ ├───all: derivation 'all'
│ │ │ ├───docker: derivation 'all'
│ │ │ └───podman: derivation 'all'
│ │ ├───ubuntu-v18_04
│ │ │ └───x86_64-linux
│ │ │ ├───all: derivation 'all'
│ │ │ ├───docker: derivation 'vm-test-run-container-test-ubuntu-v18_04'
│ │ │ └───podman: derivation 'vm-test-run-container-test-ubuntu-v18_04'
│ │ ├───ubuntu-v20_04
│ │ │ └───x86_64-linux
│ │ │ ├───all: derivation 'all'
│ │ │ ├───docker: derivation 'vm-test-run-container-test-ubuntu-v20_04'
│ │ │ └───podman: derivation 'vm-test-run-container-test-ubuntu-v20_04'
│ │ └───ubuntu-v22_04
│ │ └───x86_64-linux
│ │ ├───all: derivation 'all'
│ │ ├───docker: derivation 'vm-test-run-container-test-ubuntu-v22_04'
│ │ └───podman: derivation 'vm-test-run-container-test-ubuntu-v22_04'
```
To run all of the currently supported tests:
```bash
nix build .#hydraJobs.container-test.all.x86_64-linux.all -L
```
To run a specific distribution listed in the `nix flake show` output:
```bash
nix build .#hydraJobs.container-test.ubuntu-v22_04.x86_64-linux.docker -L
```
For PR review, you can also test arbitrary branches or checkouts like so:
```bash
nix build github:determinatesystems/nix-installer/${BRANCH}#hydraJobs.container-test.ubuntu-v22_04.x86_64-linux.podman -L
```
<details>
<summary><strong>Adding a distro?</strong></summary>
Notice how `ubuntu-v20_02` has a `v20`, not just `20`? That's so the test output shows correctly, as Nix will interpret the first `-\d` (eg `-20`, `-123213`) as a version, and not show it in the output.
Using `v20` instead turns:
```
# ...
vm-test-run-container-test-ubuntu> machine # [ 23.385182] dhcpcd[670]: vethae56f366: deleting address fe80::c036:c8ff:fe04:5832
vm-test-run-container-test-ubuntu> machine # this derivation will be built:
vm-test-run-container-test-ubuntu> machine # /nix/store/9qb0l9n1gsmcyynfmndnq3qpmlvq8rln-foo.drv
vm-test-run-container-test-ubuntu> machine # [ 23.424605] dhcpcd[670]: vethae56f366: removing interface
vm-test-run-container-test-ubuntu> machine # building '/nix/store/9qb0l9n1gsmcyynfmndnq3qpmlvq8rln-foo.drv'...
vm-test-run-container-test-ubuntu> machine # [ 23.371066] systemd[1]: crun-buildah-buildah1810857047.scope: Deactivated successfully.
# ...
```
Into this:
```
# ...
vm-test-run-container-test-ubuntu-v18_04> machine # [ 23.385182] dhcpcd[670]: vethae56f366: deleting address fe80::c036:c8ff:fe04:5832
vm-test-run-container-test-ubuntu-v20_04> machine # this derivation will be built:
vm-test-run-container-test-ubuntu-v20_04> machine # /nix/store/9qb0l9n1gsmcyynfmndnq3qpmlvq8rln-foo.drv
vm-test-run-container-test-ubuntu-v18_04> machine # [ 23.424605] dhcpcd[670]: vethae56f366: removing interface
vm-test-run-container-test-ubuntu-v20_04> machine # building '/nix/store/9qb0l9n1gsmcyynfmndnq3qpmlvq8rln-foo.drv'...
vm-test-run-container-test-ubuntu-v20_04> machine # [ 23.371066] systemd[1]: crun-buildah-buildah1810857047.scope: Deactivated successfully.
# ...
```
</details>
## WSL tests
On a Windows Machine with WSL2 enabled (and updated to [support systemd](https://ubuntu.com/blog/ubuntu-wsl-enable-systemd)) you can test using WSL the scripts in `tests/windows`:
```powershell
.\tests\windows\test-wsl.ps1
.\tests\windows\test-wsl.ps1 -Systemd
```
If something breaks you may need to unregister the test WSL instance. First, look for the distro prefixed with `nix-installer-test`:
```powershell
$ wsl --list
Windows Subsystem for Linux Distributions:
Ubuntu (Default)
nix-installer-test-ubuntu-jammy
```
Then delete it:
```powershell
wsl --unregister nix-installer-test-ubuntu-jammy
```
You can also remove your `$HOME/nix-installer-wsl-tests-temp` folder whenever you wish.
## Testing the `action.yml`
The `action.yml` is used directly in the CI process, so it is automatically tested for most changes.

121
README.md
View file

@ -16,12 +16,11 @@ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix
Current and planned support:
* [x] Multi-user Linux (aarch64 and x86_64) with systemd init, no SELinux
* [x] Multi-user MacOS (aarch64 and x86_64)
+ Note: User deletion is currently unimplemented, you need to use a user with a secure token and `dscl . -delete /Users/_nixbuild*` where `*` is each user number.
* [x] Valve Steam Deck
* [ ] Multi-user Linux (aarch64 and x86_64) with systemd init & SELinux
* [ ] Single-user Linux (aarch64 and x86_64)
* [x] Multi-user Linux (aarch64 and x86_64) with systemd integration, no SELinux
* [x] Root-only Linux (aarch64 and x86_64) with no init integration, no SELinux
* [x] Multi-user MacOS (aarch64 and x86_64) with launchd integration
* [x] SteamOS on the Valve Steam Deck
* [ ] Multi-user Linux (aarch64 and x86_64) with systemd integration & SELinux
* [ ] Others...
## Installation Differences
@ -95,16 +94,16 @@ Usage: nix-installer install linux-multi [OPTIONS]
Options:
# ...
--nix-build-user-count <NIX_BUILD_USER_COUNT>
Number of build users to create
--nix-build-group-name <NIX_BUILD_GROUP_NAME>
The Nix build group name
[env: NIX_INSTALLER_NIX_BUILD_USER_COUNT=]
[default: 32]
[env: NIX_INSTALLER_NIX_BUILD_GROUP_NAME=]
[default: nixbld]
--nix-build-user-id-base <NIX_BUILD_USER_ID_BASE>
The Nix build user base UID (ascending)
--nix-build-group-id <NIX_BUILD_GROUP_ID>
The Nix build group GID
[env: NIX_INSTALLER_NIX_BUILD_USER_ID_BASE=]
[env: NIX_INSTALLER_NIX_BUILD_GROUP_ID=]
[default: 3000]
# ...
```
@ -112,9 +111,9 @@ Options:
Planners can be configured via environment variable or command arguments:
```bash
$ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | NIX_BUILD_USER_COUNT=4 sh -s -- install linux-multi --nix-build-user-id-base 4000
$ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | NIX_BUILD_GROUP_NAME=nixbuilder sh -s -- install linux-multi --nix-build-group-id 4000
# Or...
$ NIX_BUILD_USER_COUNT=4 ./nix-installer install linux-multi --nix-build-user-id-base 4000
$ NIX_BUILD_GROUP_NAME=nixbuilder ./nix-installer install linux-multi --nix-build-group-id 4000
```
@ -145,28 +144,102 @@ jobs:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
# Allow the installed Nix to make authenticated Github requests.
# If you skip this, you will likely get rate limited.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Run `nix build`
run: nix build .
```
## In a container
## Building
In Docker/Podman containers or WSL instances where an init (like `systemd`) is not present, pass `--init none`.
> When `--init none` is used, only `root` or sudoers can run Nix:
>
> ```bash
> sudo -i nix run nixpkgs#hello
> ```
For Docker containers (without an init):
```dockerfile
# Dockerfile
FROM ubuntu:latest
RUN apt update -y
RUN apt install curl -y
COPY nix-installer /nix-installer
RUN /nix-installer install linux-multi --init none --no-confirm
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
RUN nix run nixpkgs#hello
```
Podman containers require `sandbox = false` in your `Nix.conf`.
For podman containers without an init:
```dockerfile
# Dockerfile
FROM ubuntu:latest
RUN apt update -y
RUN apt install curl -y
COPY nix-installer /nix-installer
RUN /nix-installer install linux-multi --extra-conf "sandbox = false" --init none --no-confirm
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
RUN nix run nixpkgs#hello
```
For Podman containers with an init:
```dockerfile
# Dockerfile
FROM ubuntu:latest
RUN apt update -y
RUN apt install curl systemd -y
COPY nix-installer /nix-installer
RUN /nix-installer install linux-multi --extra-conf "sandbox = false" --no-start-daemon --no-confirm
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
RUN nix run nixpkgs#hello
CMD [ "/usr/sbin/init" ]
```
## In WSL
If [systemd is enabled](https://ubuntu.com/blog/ubuntu-wsl-enable-systemd) it's possible to install Nix as normal using the command at the top of this document:
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
If systemd is not enabled, pass `--init none` at the end of the command:
> When `--init none` is used, only `root` or sudoers can run Nix:
>
> ```bash
> sudo -i nix run nixpkgs#hello
> ```
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --init none
```
## Building a binary
Since you'll be using `nix-installer` to install Nix on systems without Nix, the default build is a static binary.
Build a portable binary on a system with Nix:
Build a portable Linux binary on a system with Nix:
```bash
nix build -L github:determinatesystems/nix-installer#nix-installer-static
```
On Mac:
```bash
nix build -L github:determinatesystems/nix-installer#nix-installer
```
Then copy the `result/bin/nix-installer` to the machine you wish to run it on.
You can also add `nix-installer` to your system without having Nix:
You can also add `nix-installer` to a system without Nix via `cargo`:
```bash
RUSTFLAGS="--cfg tokio_unstable" cargo install nix-installer
@ -188,7 +261,7 @@ cargo add nix-installer
> **Building a CLI?** Check out the `cli` feature flag for `clap` integration.
You'll also need to edit your `.cargo/config.toml` to use `tokio_unstable`:
You'll also need to edit your `.cargo/config.toml` to use `tokio_unstable` as we utilize [Tokio's process groups](https://docs.rs/tokio/1.24.1/tokio/process/struct.Command.html#method.process_group), which wrap stable `std` APIs, but are unstable due to it requiring an MSRV bump:
```toml
# .cargo/config.toml
@ -208,3 +281,5 @@ Documentation is also available via `nix` build:
nix build github:DeterminateSystems/nix-installer#nix-installer.doc
firefox result-doc/nix-installer/index.html
```

View file

@ -182,6 +182,10 @@
inherit forSystem;
inherit (nix.hydraJobs) binaryTarball;
};
container-test = import ./nix/tests/container-test {
inherit forSystem;
inherit (nix.hydraJobs) binaryTarball;
};
};
};
}

View file

@ -18,8 +18,8 @@ in
runtimeInputs = with pkgs; [ git codespell ];
text = ''
codespell \
--ignore-words-list ba,sur,crate,pullrequest,pullrequests,ser \
--skip target \
--ignore-words-list ba,sur,crate,pullrequest,pullrequests,ser,distroname \
--skip target,.git \
.
'';
});

View file

@ -0,0 +1,100 @@
# Largely derived from https://github.com/NixOS/nix/blob/14f7dae3e4eb0c34192d0077383a7f2a2d630129/tests/installer/default.nix
{ forSystem, binaryTarball }:
let
images = {
# Found via https://hub.docker.com/_/ubuntu/ under "How is the rootfs build?"
# Jammy
"ubuntu-v22_04" = {
tarball = import <nix/fetchurl.nix> {
url = "https://launchpad.net/~cloud-images-release-managers/+livefs/ubuntu/jammy/ubuntu-oci/+build/408115/+files/livecd.ubuntu-oci.rootfs.tar.gz";
hash = "sha256-BirwSM4c+ZV1upU0yV3qa+BW9AvpBUxvZuPTeI9mA8M=";
};
tester = ./default/Dockerfile;
system = "x86_64-linux";
};
# focal
"ubuntu-v20_04" = {
tarball = import <nix/fetchurl.nix> {
url = "https://launchpad.net/~cloud-images-release-managers/+livefs/ubuntu/focal/ubuntu-oci/+build/408120/+files/livecd.ubuntu-oci.rootfs.tar.gz";
hash = "sha256-iTJR+DeC5lT4PMqT/xFAFwmlC/qvslDFccDrVFLt/a8=";
};
tester = ./default/Dockerfile;
system = "x86_64-linux";
};
# bionic
"ubuntu-v18_04" = {
tarball = import <nix/fetchurl.nix> {
url = "https://launchpad.net/~cloud-images-release-managers/+livefs/ubuntu/bionic/ubuntu-oci/+build/408103/+files/livecd.ubuntu-oci.rootfs.tar.gz";
hash = "sha256-gi48yl5laoKLoVCDIORsseOM6DI58FNpAjSVe7OOs7I=";
};
tester = ./default/Dockerfile;
system = "x86_64-linux";
};
};
makeTest = containerTool: imageName:
let image = images.${imageName}; in
with (forSystem image.system ({ system, pkgs, lib, ... }: pkgs));
testers.nixosTest
{
name = "container-test-${imageName}";
nodes = {
machine =
{ config, pkgs, ... }: {
virtualisation.${containerTool}.enable = true;
};
};
testScript = ''
machine.start()
machine.copy_from_host("${image.tarball}", "/image")
machine.succeed("mkdir -p /test")
machine.copy_from_host("${image.tester}", "/test/Dockerfile")
machine.copy_from_host("${nix-installer-static}", "/test/nix-installer")
machine.copy_from_host("${binaryTarball.${system}}", "/test/binary-tarball")
machine.succeed("${containerTool} import /image default")
machine.succeed("${containerTool} build -t test /test")
'';
};
container-tests = builtins.mapAttrs
(imageName: image: (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); {
${image.system} = rec {
docker = makeTest "docker" imageName;
podman = makeTest "podman" imageName;
all = pkgs.releaseTools.aggregate {
name = "all";
constituents = [
docker
podman
];
};
};
}))
images;
in
container-tests // {
all."x86_64-linux" = rec {
all = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = [
docker
podman
];
});
docker = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = pkgs.lib.mapAttrsToList (name: value: value."x86_64-linux".docker) container-tests;
});
podman = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = pkgs.lib.mapAttrsToList (name: value: value."x86_64-linux".podman) container-tests;
});
};
}

View file

@ -0,0 +1,8 @@
FROM default
COPY nix-installer /nix-installer
RUN chmod +x /nix-installer
COPY binary-tarball /binary-tarball
RUN mv /binary-tarball/nix-*.tar.xz nix.tar.xz
RUN /nix-installer/bin/nix-installer install linux-multi --nix-package-url file:///nix.tar.xz --init none --extra-conf "sandbox = false" --channel --no-confirm -vvv
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
RUN nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }'

View file

@ -5,10 +5,61 @@ let
installScripts = {
install-default = {
script = ''
install = ''
NIX_PATH=$(readlink -f nix.tar.xz)
RUST_BACKTRACE="full" ./nix-installer install --logger pretty --log-directive nix_installer=trace --channel --nix-package-url "file://$NIX_PATH" --no-confirm
'';
check = ''
set -ex
nix-env --version
nix --extra-experimental-features nix-command store ping
out=$(nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }')
[[ $(cat $out) = foobar ]]
'';
};
install-no-start-daemon = {
install = ''
NIX_PATH=$(readlink -f nix.tar.xz)
RUST_BACKTRACE="full" ./nix-installer install linux-multi --no-start-daemon --logger pretty --log-directive nix_installer=trace --channel --nix-package-url "file://$NIX_PATH" --no-confirm
'';
check = ''
set -ex
if systemctl is-active nix-daemon.socket; then
echo "nix-daemon.socket was running, should not be"
exit 1
fi
if systemctl is-active nix-daemon.service; then
echo "nix-daemon.service was running, should not be"
exit 1
fi
sudo systemctl start nix-daemon.socket
nix-env --version
nix --extra-experimental-features nix-command store ping
out=$(nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }')
[[ $(cat $out) = foobar ]]
'';
};
install-daemonless = {
install = ''
NIX_PATH=$(readlink -f nix.tar.xz)
RUST_BACKTRACE="full" ./nix-installer install linux-multi --init none --logger pretty --log-directive nix_installer=trace --channel --nix-package-url "file://$NIX_PATH" --no-confirm
'';
check = ''
set -ex
sudo -i nix-env --version
sudo -i nix --extra-experimental-features nix-command store ping
echo 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }' | sudo tee -a /drv
out=$(sudo -i nix-build --no-substitute /drv)
[[ $(cat $out) = foobar ]]
'';
};
};
@ -114,7 +165,8 @@ let
buildInputs = [ qemu_kvm openssh ];
image = image.image;
postBoot = image.postBoot or "";
installScript = installScripts.${testName}.script;
installScript = installScripts.${testName}.install;
checkScript = installScripts.${testName}.check;
installer = nix-installer-static;
binaryTarball = binaryTarball.${system};
}
@ -182,15 +234,7 @@ let
$ssh "set -eux; $installScript"
echo "Testing Nix installation..."
$ssh <<EOF
set -ex
nix-env --version
nix --extra-experimental-features nix-command store ping
out=\$(nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > \$out"]; }')
[[ \$(cat \$out) = foobar ]]
EOF
$ssh "set -eux; $checkScript"
echo "Done!"
touch $out
@ -198,20 +242,47 @@ let
vm-tests = builtins.mapAttrs
(imageName: image:
{
${image.system} = builtins.mapAttrs
rec {
${image.system} = (builtins.mapAttrs
(testName: test:
makeTest imageName testName
)
installScripts;
installScripts) // {
all = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = (
pkgs.lib.mapAttrsToList
(testName: test:
makeTest imageName testName
)
installScripts
);
});
};
}
)
images;
in
vm-tests // {
vm-tests // rec {
all."x86_64-linux".install-default = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = pkgs.lib.mapAttrsToList (name: value: value."x86_64-linux".install-default) vm-tests;
});
all."x86_64-linux".install-no-start-daemon = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = pkgs.lib.mapAttrsToList (name: value: value."x86_64-linux".install-default) vm-tests;
});
all."x86_64-linux".install-daemonless = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = pkgs.lib.mapAttrsToList (name: value: value."x86_64-linux".install-daemonless) vm-tests;
});
all."x86_64-linux".all = (with (forSystem "x86_64-linux" ({ system, pkgs, ... }: pkgs)); pkgs.releaseTools.aggregate {
name = "all";
constituents = [
all."x86_64-linux".install-default
all."x86_64-linux".install-no-start-daemon
all."x86_64-linux".install-daemonless
];
});
}

View file

@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
use crate::{
action::{ActionError, StatefulAction},
execute_command, set_env,
execute_command, set_env, ChannelValue,
};
use glob::glob;
@ -17,12 +17,12 @@ Setup the default Nix profile with `nss-cacert` and `nix` itself.
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct SetupDefaultProfile {
channels: Vec<String>,
channels: Vec<ChannelValue>,
}
impl SetupDefaultProfile {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(channels: Vec<String>) -> Result<StatefulAction<Self>, ActionError> {
pub async fn plan(channels: Vec<ChannelValue>) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { channels }.into())
}
}
@ -38,7 +38,12 @@ impl Action for SetupDefaultProfile {
span!(
tracing::Level::DEBUG,
"setup_default_profile",
channels = self.channels.join(","),
channels = self
.channels
.iter()
.map(|ChannelValue(channel, url)| format!("{channel}={url}"))
.collect::<Vec<_>>()
.join(","),
)
}
@ -206,7 +211,7 @@ impl Action for SetupDefaultProfile {
command.process_group(0);
command.arg("--update");
for channel in channels {
command.arg(channel);
command.arg(channel.0.clone());
}
command.env(
"NIX_SSL_CERT_FILE",

View file

@ -1,7 +1,5 @@
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use target_lexicon::OperatingSystem;
use tokio::fs::remove_file;
use tokio::process::Command;
use tracing::{span, Span};
@ -9,48 +7,54 @@ use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::action::{Action, ActionDescription};
use crate::settings::InitSystem;
#[cfg(target_os = "linux")]
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
#[cfg(target_os = "linux")]
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
#[cfg(target_os = "linux")]
const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default/lib/tmpfiles.d/nix-daemon.conf";
#[cfg(target_os = "linux")]
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
#[cfg(target_os = "macos")]
const DARWIN_NIX_DAEMON_DEST: &str = "/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
#[cfg(target_os = "macos")]
const DARWIN_NIX_DAEMON_SOURCE: &str =
"/nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
/**
Run systemd utilities to configure the Nix daemon
Configure the init to run the Nix daemon
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureNixDaemonService {}
pub struct ConfigureInitService {
init: InitSystem,
start_daemon: bool,
}
impl ConfigureNixDaemonService {
impl ConfigureInitService {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
}
| OperatingSystem::Darwin => (),
_ => {
if !Path::new("/run/systemd/system").exists() {
return Err(ActionError::Custom(Box::new(
ConfigureNixDaemonServiceError::InitNotSupported,
)));
}
},
};
Ok(Self {}.into())
pub async fn plan(
init: InitSystem,
start_daemon: bool,
) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { init, start_daemon }.into())
}
}
#[async_trait::async_trait]
#[typetag::serde(name = "configure_nix_daemon")]
impl Action for ConfigureNixDaemonService {
impl Action for ConfigureInitService {
fn tracing_synopsis(&self) -> String {
"Configure Nix daemon related settings with systemd".to_string()
match self.init {
#[cfg(target_os = "linux")]
InitSystem::Systemd => "Configure Nix daemon related settings with systemd".to_string(),
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
"Configure Nix daemon related settings with launchctl".to_string()
},
#[cfg(not(target_os = "macos"))]
InitSystem::None => "Leave the Nix daemon unconfigured".to_string(),
}
}
fn tracing_span(&self) -> Span {
@ -58,44 +62,45 @@ impl Action for ConfigureNixDaemonService {
}
fn execute_description(&self) -> Vec<ActionDescription> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
}
| OperatingSystem::Darwin => vec![ActionDescription::new(
self.tracing_synopsis(),
vec![
format!("Copy `{DARWIN_NIX_DAEMON_SOURCE}` to `DARWIN_NIX_DAEMON_DEST`"),
format!("Run `launchctl load {DARWIN_NIX_DAEMON_DEST}`"),
],
)],
_ => vec![ActionDescription::new(
self.tracing_synopsis(),
vec![
let mut vec = Vec::new();
match self.init {
#[cfg(target_os = "linux")]
InitSystem::Systemd => {
let mut explanation = vec![
"Run `systemd-tempfiles --create --prefix=/nix/var/nix`".to_string(),
format!("Run `systemctl link {SERVICE_SRC}`"),
format!("Run `systemctl link {SOCKET_SRC}`"),
"Run `systemctl daemon-reload`".to_string(),
format!("Run `systemctl enable --now {SOCKET_SRC}`"),
],
)],
];
if self.start_daemon {
explanation.push(format!("Run `systemctl enable --now {SOCKET_SRC}`"));
}
vec.push(ActionDescription::new(self.tracing_synopsis(), explanation))
},
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
let mut explanation = vec![format!(
"Copy `{DARWIN_NIX_DAEMON_SOURCE}` to `DARWIN_NIX_DAEMON_DEST`"
)];
if self.start_daemon {
explanation.push(format!("Run `launchctl load {DARWIN_NIX_DAEMON_DEST}`"));
}
vec.push(ActionDescription::new(self.tracing_synopsis(), explanation))
},
#[cfg(not(target_os = "macos"))]
InitSystem::None => (),
}
vec
}
#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {} = self;
let Self { init, start_daemon } = self;
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
}
| OperatingSystem::Darwin => {
let src = Path::new(DARWIN_NIX_DAEMON_SOURCE);
match init {
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
let src = std::path::Path::new(DARWIN_NIX_DAEMON_SOURCE);
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
.await
.map_err(|e| {
@ -115,8 +120,22 @@ impl Action for ConfigureNixDaemonService {
)
.await
.map_err(ActionError::Command)?;
if *start_daemon {
execute_command(
Command::new("launchctl")
.process_group(0)
.arg("kickstart")
.arg("-k")
.arg("system/org.nixos.nix-daemon")
.stdin(std::process::Stdio::null()),
)
.await
.map_err(ActionError::Command)?;
}
},
_ => {
#[cfg(target_os = "linux")]
InitSystem::Systemd => {
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await
@ -158,24 +177,30 @@ impl Action for ConfigureNixDaemonService {
.await
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
.process_group(0)
.arg("daemon-reload")
.stdin(std::process::Stdio::null()),
)
.await
.map_err(ActionError::Command)?;
if *start_daemon {
execute_command(
Command::new("systemctl")
.process_group(0)
.arg("daemon-reload")
.stdin(std::process::Stdio::null()),
)
.await
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
.process_group(0)
.arg("enable")
.arg("--now")
.arg(SOCKET_SRC),
)
.await
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
.process_group(0)
.arg("enable")
.arg("--now")
.arg(SOCKET_SRC),
)
.await
.map_err(ActionError::Command)?;
}
},
#[cfg(not(target_os = "macos"))]
InitSystem::None => {
// Nothing here, no init system
},
};
@ -183,37 +208,36 @@ impl Action for ConfigureNixDaemonService {
}
fn revert_description(&self) -> Vec<ActionDescription> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
}
| OperatingSystem::Darwin => vec![ActionDescription::new(
"Unconfigure Nix daemon related settings with launchd".to_string(),
vec!["Run `launchctl unload {DARWIN_NIX_DAEMON_DEST}`".to_string()],
)],
_ => vec![ActionDescription::new(
"Unconfigure Nix daemon related settings with systemd".to_string(),
vec![
"Run `systemctl disable {SOCKET_SRC}`".to_string(),
"Run `systemctl disable {SERVICE_SRC}`".to_string(),
"Run `systemd-tempfiles --remove --prefix=/nix/var/nix`".to_string(),
"Run `systemctl daemon-reload`".to_string(),
],
)],
match self.init {
#[cfg(target_os = "linux")]
InitSystem::Systemd => {
vec![ActionDescription::new(
"Unconfigure Nix daemon related settings with systemd".to_string(),
vec![
"Run `systemctl disable {SOCKET_SRC}`".to_string(),
"Run `systemctl disable {SERVICE_SRC}`".to_string(),
"Run `systemd-tempfiles --remove --prefix=/nix/var/nix`".to_string(),
"Run `systemctl daemon-reload`".to_string(),
],
)]
},
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
vec![ActionDescription::new(
"Unconfigure Nix daemon related settings with launchctl".to_string(),
vec!["Run `launchctl unload {DARWIN_NIX_DAEMON_DEST}`".to_string()],
)]
},
#[cfg(not(target_os = "macos"))]
InitSystem::None => Vec::new(),
}
}
#[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
}
| OperatingSystem::Darwin => {
match self.init {
#[cfg(target_os = "macos")]
InitSystem::Launchd => {
execute_command(
Command::new("launchctl")
.process_group(0)
@ -223,7 +247,8 @@ impl Action for ConfigureNixDaemonService {
.await
.map_err(ActionError::Command)?;
},
_ => {
#[cfg(target_os = "linux")]
InitSystem::Systemd => {
// We separate stop and disable (instead of using `--now`) to avoid cases where the service isn't started, but is enabled.
let socket_is_active = is_active("nix-daemon.socket").await?;
@ -285,7 +310,7 @@ impl Action for ConfigureNixDaemonService {
.await
.map_err(ActionError::Command)?;
remove_file(TMPFILES_DEST)
tokio::fs::remove_file(TMPFILES_DEST)
.await
.map_err(|e| ActionError::Remove(PathBuf::from(TMPFILES_DEST), e))?;
@ -298,6 +323,10 @@ impl Action for ConfigureNixDaemonService {
.await
.map_err(ActionError::Command)?;
},
#[cfg(not(target_os = "macos"))]
InitSystem::None => {
// Nothing here, no init
},
};
Ok(())
@ -310,6 +339,7 @@ pub enum ConfigureNixDaemonServiceError {
InitNotSupported,
}
#[cfg(target_os = "linux")]
async fn is_active(unit: &str) -> Result<bool, ActionError> {
let output = Command::new("systemctl")
.arg("is-active")
@ -326,6 +356,7 @@ async fn is_active(unit: &str) -> Result<bool, ActionError> {
}
}
#[cfg(target_os = "linux")]
async fn is_enabled(unit: &str) -> Result<bool, ActionError> {
let output = Command::new("systemctl")
.arg("is-enabled")

View file

@ -1,17 +1,12 @@
use crate::{
action::{
base::SetupDefaultProfile,
common::{
ConfigureNixDaemonService, ConfigureShellProfile, PlaceChannelConfiguration,
PlaceNixConfiguration,
},
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
Action, ActionDescription, ActionError, StatefulAction,
},
channel_value::ChannelValue,
settings::CommonSettings,
};
use reqwest::Url;
use tracing::{span, Instrument, Span};
/**
@ -23,20 +18,12 @@ pub struct ConfigureNix {
configure_shell_profile: Option<StatefulAction<ConfigureShellProfile>>,
place_channel_configuration: StatefulAction<PlaceChannelConfiguration>,
place_nix_configuration: StatefulAction<PlaceNixConfiguration>,
configure_nix_daemon_service: StatefulAction<ConfigureNixDaemonService>,
}
impl ConfigureNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let channels: Vec<(String, Url)> = settings
.channels
.iter()
.map(|ChannelValue(channel, url)| (channel.to_string(), url.clone()))
.collect();
let setup_default_profile =
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?;
let setup_default_profile = SetupDefaultProfile::plan(settings.channels.clone()).await?;
let configure_shell_profile = if settings.modify_profile {
Some(ConfigureShellProfile::plan().await?)
@ -44,20 +31,18 @@ impl ConfigureNix {
None
};
let place_channel_configuration =
PlaceChannelConfiguration::plan(channels, settings.force).await?;
PlaceChannelConfiguration::plan(settings.channels.clone(), settings.force).await?;
let place_nix_configuration = PlaceNixConfiguration::plan(
settings.nix_build_group_name.clone(),
settings.extra_conf.clone(),
settings.force,
)
.await?;
let configure_nix_daemon_service = ConfigureNixDaemonService::plan().await?;
Ok(Self {
place_channel_configuration,
place_nix_configuration,
setup_default_profile,
configure_nix_daemon_service,
configure_shell_profile,
}
.into())
@ -78,14 +63,12 @@ impl Action for ConfigureNix {
fn execute_description(&self) -> Vec<ActionDescription> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
} = &self;
let mut buf = setup_default_profile.describe_execute();
buf.append(&mut configure_nix_daemon_service.describe_execute());
buf.append(&mut place_nix_configuration.describe_execute());
buf.append(&mut place_channel_configuration.describe_execute());
if let Some(configure_shell_profile) = configure_shell_profile {
@ -98,7 +81,6 @@ impl Action for ConfigureNix {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
@ -168,7 +150,6 @@ impl Action for ConfigureNix {
},
)?;
};
configure_nix_daemon_service.try_execute().await?;
Ok(())
}
@ -176,7 +157,6 @@ impl Action for ConfigureNix {
fn revert_description(&self) -> Vec<ActionDescription> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
@ -188,7 +168,6 @@ impl Action for ConfigureNix {
}
buf.append(&mut place_channel_configuration.describe_revert());
buf.append(&mut place_nix_configuration.describe_revert());
buf.append(&mut configure_nix_daemon_service.describe_revert());
buf.append(&mut setup_default_profile.describe_revert());
buf
@ -198,13 +177,11 @@ impl Action for ConfigureNix {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
} = self;
configure_nix_daemon_service.try_revert().await?;
if let Some(configure_shell_profile) = configure_shell_profile {
configure_shell_profile.try_revert().await?;
}

View file

@ -1,7 +1,7 @@
//! [`Action`](crate::action::Action)s which only call other base plugins
pub(crate) mod configure_init_service;
pub(crate) mod configure_nix;
pub(crate) mod configure_nix_daemon_service;
pub(crate) mod configure_shell_profile;
pub(crate) mod create_nix_tree;
pub(crate) mod create_users_and_groups;
@ -9,8 +9,8 @@ pub(crate) mod place_channel_configuration;
pub(crate) mod place_nix_configuration;
pub(crate) mod provision_nix;
pub use configure_init_service::{ConfigureInitService, ConfigureNixDaemonServiceError};
pub use configure_nix::ConfigureNix;
pub use configure_nix_daemon_service::{ConfigureNixDaemonService, ConfigureNixDaemonServiceError};
pub use configure_shell_profile::ConfigureShellProfile;
pub use create_nix_tree::CreateNixTree;
pub use create_users_and_groups::CreateUsersAndGroups;

View file

@ -1,7 +1,7 @@
use crate::action::base::CreateFile;
use crate::action::ActionError;
use crate::action::{Action, ActionDescription, StatefulAction};
use reqwest::Url;
use crate::ChannelValue;
use tracing::{span, Span};
/**
@ -9,19 +9,19 @@ Place a channel configuration containing `channels` to the `$ROOT_HOME/.nix-chan
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct PlaceChannelConfiguration {
channels: Vec<(String, Url)>,
channels: Vec<ChannelValue>,
create_file: StatefulAction<CreateFile>,
}
impl PlaceChannelConfiguration {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(
channels: Vec<(String, Url)>,
channels: Vec<ChannelValue>,
force: bool,
) -> Result<StatefulAction<Self>, ActionError> {
let buf = channels
.iter()
.map(|(name, url)| format!("{} {}", url, name))
.map(|ChannelValue(name, url)| format!("{} {}", url, name))
.collect::<Vec<_>>()
.join("\n");
let create_file = CreateFile::plan(
@ -62,7 +62,7 @@ impl Action for PlaceChannelConfiguration {
channels = self
.channels
.iter()
.map(|(c, u)| format!("{c}={u}"))
.map(|ChannelValue(c, u)| format!("{c}={u}"))
.collect::<Vec<_>>()
.join(", "),
)

View file

@ -1,19 +1,13 @@
use nix::unistd::{chown, Group, User};
use uuid::Uuid;
use crate::{
action::{Action, ActionDescription, ActionError, StatefulAction},
execute_command,
};
use rand::Rng;
use serde::Deserialize;
use std::{
io::SeekFrom,
path::{Path, PathBuf},
str::FromStr,
};
use std::{io::SeekFrom, path::Path};
use tokio::{
fs::{remove_file, OpenOptions},
fs::OpenOptions,
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
process::Command,
};

View file

@ -1,71 +0,0 @@
use tokio::process::Command;
use tracing::{span, Span};
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::action::{Action, ActionDescription};
/**
Kickstart a `launchctl` service
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct KickstartLaunchctlService {
unit: String,
}
impl KickstartLaunchctlService {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(unit: String) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { unit }.into())
}
}
#[async_trait::async_trait]
#[typetag::serde(name = "kickstart_launchctl_service")]
impl Action for KickstartLaunchctlService {
fn tracing_synopsis(&self) -> String {
let Self { unit, .. } = self;
format!("Kickstart the launchctl unit `{unit}`")
}
fn tracing_span(&self) -> Span {
span!(
tracing::Level::DEBUG,
"kickstart_launchctl_service",
unit = %self.unit,
)
}
fn execute_description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new(self.tracing_synopsis(), vec![])]
}
#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { unit } = self;
execute_command(
Command::new("launchctl")
.process_group(0)
.arg("kickstart")
.arg("-k")
.arg(unit)
.stdin(std::process::Stdio::null()),
)
.await
.map_err(ActionError::Command)?;
Ok(())
}
fn revert_description(&self) -> Vec<ActionDescription> {
vec![]
}
#[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> {
// noop
Ok(())
}
}

View file

@ -8,7 +8,6 @@ pub(crate) mod create_nix_volume;
pub(crate) mod create_synthetic_objects;
pub(crate) mod enable_ownership;
pub(crate) mod encrypt_apfs_volume;
pub(crate) mod kickstart_launchctl_service;
pub(crate) mod unmount_apfs_volume;
pub use bootstrap_apfs_volume::{BootstrapApfsVolume, BootstrapVolumeError};
@ -17,5 +16,4 @@ pub use create_nix_volume::{CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
pub use encrypt_apfs_volume::EncryptApfsVolume;
pub use kickstart_launchctl_service::KickstartLaunchctlService;
pub use unmount_apfs_volume::UnmountApfsVolume;

View file

@ -32,7 +32,7 @@ action.try_revert().await.unwrap();
```
A general guidance for what determines how fine-grained an [`Action`] should be is the unit of
reversion. The [`ConfigureNixDaemonService`](common::ConfigureNixDaemonService) action is a good
reversion. The [`ConfigureInitService`](common::ConfigureInitService) action is a good
example of this, it takes several steps, such as running `systemd-tmpfiles`, and calling
`systemctl link` on some systemd units.
@ -49,7 +49,7 @@ use tracing::{Span, span};
use nix_installer::{
InstallPlan,
settings::{CommonSettings, InstallSettingsError},
planner::{Planner, PlannerError, linux::SteamDeck},
planner::{Planner, PlannerError},
action::{Action, ActionError, StatefulAction, ActionDescription},
};
@ -112,7 +112,7 @@ pub struct MyPlanner {
impl Planner for MyPlanner {
async fn default() -> Result<Self, PlannerError> {
Ok(Self {
common: CommonSettings::default()?,
common: CommonSettings::default().await?,
})
}

View file

@ -34,14 +34,17 @@ match plan.install(None).await {
# }
```
Sometimes choosing a specific plan is desired:
Sometimes choosing a specific planner is desired:
```rust,no_run
use std::error::Error;
use nix_installer::{InstallPlan, planner::{Planner, linux::SteamDeck}};
use nix_installer::{InstallPlan, planner::Planner};
# async fn chosen_planner_install() -> color_eyre::Result<()> {
let planner = SteamDeck::default().await?;
#[cfg(target_os = "linux")]
let planner = nix_installer::planner::linux::SteamDeck::default().await?;
#[cfg(target_os = "macos")]
let planner = nix_installer::planner::darwin::DarwinMulti::default().await?;
// Or call `crate::planner::BuiltinPlanner::default()`
// Match on the result to customize.

View file

@ -6,15 +6,15 @@ use tokio::process::Command;
use crate::{
action::{
common::{ConfigureNix, ProvisionNix},
darwin::{CreateNixVolume, KickstartLaunchctlService},
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
darwin::CreateNixVolume,
StatefulAction,
},
execute_command,
os::darwin::DiskUtilOutput,
planner::{Planner, PlannerError},
settings::CommonSettings,
settings::InstallSettingsError,
settings::{CommonSettings, InitSystem},
Action, BuiltinPlanner,
};
@ -24,6 +24,7 @@ use crate::{
pub struct DarwinMulti {
#[cfg_attr(feature = "cli", clap(flatten))]
pub settings: CommonSettings,
/// Force encryption on the volume
#[cfg_attr(
feature = "cli",
@ -76,7 +77,7 @@ async fn default_root_disk() -> Result<String, PlannerError> {
impl Planner for DarwinMulti {
async fn default() -> Result<Self, PlannerError> {
Ok(Self {
settings: CommonSettings::default()?,
settings: CommonSettings::default().await?,
root_disk: Some(default_root_disk().await?),
case_sensitive: false,
encrypt: None,
@ -140,7 +141,7 @@ impl Planner for DarwinMulti {
.await
.map_err(PlannerError::Action)?
.boxed(),
KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into())
ConfigureInitService::plan(InitSystem::Launchd, true)
.await
.map_err(PlannerError::Action)?
.boxed(),

View file

@ -1,12 +1,12 @@
use crate::{
action::{
base::CreateDirectory,
common::{ConfigureNix, ProvisionNix},
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
StatefulAction,
},
planner::{Planner, PlannerError},
settings::CommonSettings,
settings::InstallSettingsError,
settings::{InitSettings, InstallSettingsError},
Action, BuiltinPlanner,
};
use std::{collections::HashMap, path::Path};
@ -18,6 +18,8 @@ use tokio::process::Command;
pub struct LinuxMulti {
#[cfg_attr(feature = "cli", clap(flatten))]
pub settings: CommonSettings,
#[cfg_attr(feature = "cli", clap(flatten))]
pub init: InitSettings,
}
#[async_trait::async_trait]
@ -25,7 +27,8 @@ pub struct LinuxMulti {
impl Planner for LinuxMulti {
async fn default() -> Result<Self, PlannerError> {
Ok(Self {
settings: CommonSettings::default()?,
settings: CommonSettings::default().await?,
init: InitSettings::default().await?,
})
}
@ -77,14 +80,19 @@ impl Planner for LinuxMulti {
.await
.map_err(PlannerError::Action)?
.boxed(),
ConfigureInitService::plan(self.init.init, self.init.start_daemon)
.await
.map_err(PlannerError::Action)?
.boxed(),
])
}
fn settings(&self) -> Result<HashMap<String, serde_json::Value>, InstallSettingsError> {
let Self { settings } = self;
let Self { settings, init } = self;
let mut map = HashMap::default();
map.extend(settings.settings()?.into_iter());
map.extend(init.settings()?.into_iter());
Ok(map)
}

View file

@ -64,12 +64,12 @@ use std::{collections::HashMap, path::PathBuf};
use crate::{
action::{
base::{CreateDirectory, CreateFile},
common::{ConfigureNix, ProvisionNix},
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
linux::StartSystemdUnit,
Action, StatefulAction,
},
planner::{Planner, PlannerError},
settings::{CommonSettings, InstallSettingsError},
settings::{CommonSettings, InitSystem, InstallSettingsError},
BuiltinPlanner,
};
@ -95,7 +95,7 @@ impl Planner for SteamDeck {
async fn default() -> Result<Self, PlannerError> {
Ok(Self {
persistence: PathBuf::from("/home/nix"),
settings: CommonSettings::default()?,
settings: CommonSettings::default().await?,
})
}
@ -225,6 +225,11 @@ impl Planner for SteamDeck {
.await
.map_err(PlannerError::Action)?
.boxed(),
// Init is required for the steam-deck archetype to make the `/nix` mount
ConfigureInitService::plan(InitSystem::Systemd, true)
.await
.map_err(PlannerError::Action)?
.boxed(),
])
}

View file

@ -1,7 +1,6 @@
/*! [`BuiltinPlanner`]s and traits to create new types which can be used to plan out an [`InstallPlan`]
It's a [`Planner`]s job to construct (if possible) a valid [`InstallPlan`] for the host. Some planners,
like [`LinuxMulti`](linux::LinuxMulti), are operating system specific. Others, like [`SteamDeck`](linux::SteamDeck), are device specific.
It's a [`Planner`]s job to construct (if possible) a valid [`InstallPlan`] for the host. Some planners are operating system specific, others are device specific.
[`Planner`]s contain their planner specific settings, typically alongside a [`CommonSettings`][crate::settings::CommonSettings].
@ -16,8 +15,8 @@ use std::{error::Error, collections::HashMap};
use nix_installer::{
InstallPlan,
settings::{CommonSettings, InstallSettingsError},
planner::{Planner, PlannerError, linux::SteamDeck},
action::{Action, StatefulAction, linux::StartSystemdUnit},
planner::{Planner, PlannerError},
action::{Action, StatefulAction, base::CreateFile},
};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
@ -31,7 +30,7 @@ pub struct MyPlanner {
impl Planner for MyPlanner {
async fn default() -> Result<Self, PlannerError> {
Ok(Self {
common: CommonSettings::default()?,
common: CommonSettings::default().await?,
})
}
@ -39,7 +38,7 @@ impl Planner for MyPlanner {
Ok(vec![
// ...
StartSystemdUnit::plan("nix-daemon.socket")
CreateFile::plan("/example", None, None, None, "Example".to_string(), false)
.await
.map_err(PlannerError::Action)?.boxed(),
])
@ -74,7 +73,9 @@ match plan.install(None).await {
```
*/
#[cfg(target_os = "macos")]
pub mod darwin;
#[cfg(target_os = "linux")]
pub mod linux;
use std::{collections::HashMap, string::FromUtf8Error};
@ -113,11 +114,14 @@ dyn_clone::clone_trait_object!(Planner);
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "cli", derive(clap::Subcommand))]
pub enum BuiltinPlanner {
#[cfg(target_os = "linux")]
/// A standard Linux multi-user install
LinuxMulti(linux::LinuxMulti),
/// A standard MacOS (Darwin) multi-user install
#[cfg(target_os = "macos")]
DarwinMulti(darwin::DarwinMulti),
/// A specialized install suitable for the Valve Steam Deck console
#[cfg(target_os = "linux")]
SteamDeck(linux::SteamDeck),
}
@ -126,16 +130,20 @@ impl BuiltinPlanner {
pub async fn default() -> Result<Self, PlannerError> {
use target_lexicon::{Architecture, OperatingSystem};
match (Architecture::host(), OperatingSystem::host()) {
#[cfg(target_os = "linux")]
(Architecture::X86_64, OperatingSystem::Linux) => {
Ok(Self::LinuxMulti(linux::LinuxMulti::default().await?))
},
#[cfg(target_os = "linux")]
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
Ok(Self::LinuxMulti(linux::LinuxMulti::default().await?))
},
#[cfg(target_os = "macos")]
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => {
Ok(Self::DarwinMulti(darwin::DarwinMulti::default().await?))
},
#[cfg(target_os = "macos")]
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
Ok(Self::DarwinMulti(darwin::DarwinMulti::default().await?))
@ -147,41 +155,56 @@ impl BuiltinPlanner {
pub async fn from_common_settings(settings: CommonSettings) -> Result<Self, PlannerError> {
let mut built = Self::default().await?;
match &mut built {
#[cfg(target_os = "linux")]
BuiltinPlanner::LinuxMulti(inner) => inner.settings = settings,
BuiltinPlanner::DarwinMulti(inner) => inner.settings = settings,
#[cfg(target_os = "linux")]
BuiltinPlanner::SteamDeck(inner) => inner.settings = settings,
#[cfg(target_os = "macos")]
BuiltinPlanner::DarwinMulti(inner) => inner.settings = settings,
}
Ok(built)
}
pub async fn plan(self) -> Result<InstallPlan, NixInstallerError> {
match self {
#[cfg(target_os = "linux")]
BuiltinPlanner::LinuxMulti(planner) => InstallPlan::plan(planner).await,
BuiltinPlanner::DarwinMulti(planner) => InstallPlan::plan(planner).await,
#[cfg(target_os = "linux")]
BuiltinPlanner::SteamDeck(planner) => InstallPlan::plan(planner).await,
#[cfg(target_os = "macos")]
BuiltinPlanner::DarwinMulti(planner) => InstallPlan::plan(planner).await,
}
}
pub fn boxed(self) -> Box<dyn Planner> {
match self {
#[cfg(target_os = "linux")]
BuiltinPlanner::LinuxMulti(i) => i.boxed(),
BuiltinPlanner::DarwinMulti(i) => i.boxed(),
#[cfg(target_os = "linux")]
BuiltinPlanner::SteamDeck(i) => i.boxed(),
#[cfg(target_os = "macos")]
BuiltinPlanner::DarwinMulti(i) => i.boxed(),
}
}
pub fn typetag_name(&self) -> &'static str {
match self {
#[cfg(target_os = "linux")]
BuiltinPlanner::LinuxMulti(i) => i.typetag_name(),
BuiltinPlanner::DarwinMulti(i) => i.typetag_name(),
#[cfg(target_os = "linux")]
BuiltinPlanner::SteamDeck(i) => i.typetag_name(),
#[cfg(target_os = "macos")]
BuiltinPlanner::DarwinMulti(i) => i.typetag_name(),
}
}
pub fn settings(&self) -> Result<HashMap<String, serde_json::Value>, InstallSettingsError> {
match self {
#[cfg(target_os = "linux")]
BuiltinPlanner::LinuxMulti(i) => i.settings(),
BuiltinPlanner::DarwinMulti(i) => i.settings(),
#[cfg(target_os = "linux")]
BuiltinPlanner::SteamDeck(i) => i.settings(),
#[cfg(target_os = "macos")]
BuiltinPlanner::DarwinMulti(i) => i.settings(),
}
}
}

View file

@ -21,6 +21,30 @@ pub const NIX_X64_64_DARWIN_URL: &str =
pub const NIX_AARCH64_DARWIN_URL: &str =
"https://releases.nixos.org/nix/nix-2.12.0/nix-2.12.0-aarch64-darwin.tar.xz";
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Copy)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum InitSystem {
#[cfg(not(target_os = "macos"))]
None,
#[cfg(target_os = "linux")]
Systemd,
#[cfg(target_os = "macos")]
Launchd,
}
impl std::fmt::Display for InitSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(not(target_os = "macos"))]
InitSystem::None => write!(f, "none"),
#[cfg(target_os = "linux")]
InitSystem::Systemd => write!(f, "systemd"),
#[cfg(target_os = "macos")]
InitSystem::Launchd => write!(f, "launchd"),
}
}
}
/** Common settings used by all [`BuiltinPlanner`](crate::planner::BuiltinPlanner)s
Settings which only apply to certain [`Planner`](crate::planner::Planner)s should be located in the planner.
@ -173,29 +197,33 @@ pub struct CommonSettings {
impl CommonSettings {
/// The default settings for the given Architecture & Operating System
pub fn default() -> Result<Self, InstallSettingsError> {
pub async fn default() -> Result<Self, InstallSettingsError> {
let url;
let nix_build_user_prefix;
let nix_build_user_id_base;
use target_lexicon::{Architecture, OperatingSystem};
match (Architecture::host(), OperatingSystem::host()) {
#[cfg(target_os = "linux")]
(Architecture::X86_64, OperatingSystem::Linux) => {
url = NIX_X64_64_LINUX_URL;
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 3000;
},
#[cfg(target_os = "linux")]
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
url = NIX_AARCH64_LINUX_URL;
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 3000;
},
#[cfg(target_os = "macos")]
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => {
url = NIX_X64_64_DARWIN_URL;
nix_build_user_prefix = "_nixbld";
nix_build_user_id_base = 300;
},
#[cfg(target_os = "macos")]
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
url = NIX_AARCH64_DARWIN_URL;
@ -286,6 +314,34 @@ impl CommonSettings {
Ok(map)
}
}
#[cfg(target_os = "linux")]
async fn linux_detect_init() -> (InitSystem, bool) {
use std::process::Stdio;
let mut detected = InitSystem::None;
let mut started = false;
if std::path::Path::new("/run/systemd/system").exists() {
detected = InitSystem::Systemd;
started = if tokio::process::Command::new("systemctl")
.arg("status")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.await
.ok()
.map(|exit| exit.success())
.unwrap_or(false)
{
true
} else {
false
}
}
// TODO: Other inits
(detected, started)
}
// Builder Pattern
impl CommonSettings {
@ -349,6 +405,96 @@ impl CommonSettings {
}
}
#[serde_with::serde_as]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg_attr(feature = "cli", derive(clap::Parser))]
pub struct InitSettings {
/// Which init system to configure (if `--init none` Nix will be root-only)
#[cfg_attr(feature = "cli", clap(value_parser, long, env = "NIX_INSTALLER_INIT",))]
#[cfg_attr(
all(target_os = "macos", feature = "cli"),
clap(default_value_t = InitSystem::Launchd)
)]
#[cfg_attr(
all(target_os = "linux", feature = "cli"),
clap(default_value_t = InitSystem::Systemd)
)]
pub(crate) init: InitSystem,
/// Start the daemon (if not `--init none`)
#[cfg_attr(
feature = "cli",
clap(
value_parser,
long,
action(ArgAction::SetFalse),
env = "NIX_INSTALLER_START_DAEMON",
default_value_t = true,
long = "no-start-daemon"
)
)]
pub(crate) start_daemon: bool,
}
impl InitSettings {
/// The default settings for the given Architecture & Operating System
pub async fn default() -> Result<Self, InstallSettingsError> {
let init;
let start_daemon;
use target_lexicon::{Architecture, OperatingSystem};
match (Architecture::host(), OperatingSystem::host()) {
#[cfg(target_os = "linux")]
(Architecture::X86_64, OperatingSystem::Linux) => {
(init, start_daemon) = linux_detect_init().await;
},
#[cfg(target_os = "linux")]
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
(init, start_daemon) = linux_detect_init().await;
},
#[cfg(target_os = "macos")]
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => {
(init, start_daemon) = (InitSystem::Launchd, true);
},
#[cfg(target_os = "macos")]
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
(init, start_daemon) = (InitSystem::Launchd, true);
},
_ => {
return Err(InstallSettingsError::UnsupportedArchitecture(
target_lexicon::HOST,
))
},
};
Ok(Self { init, start_daemon })
}
/// A listing of the settings, suitable for [`Planner::settings`](crate::planner::Planner::settings)
pub fn settings(&self) -> Result<HashMap<String, serde_json::Value>, InstallSettingsError> {
let Self { init, start_daemon } = self;
let mut map = HashMap::default();
map.insert("init".into(), serde_json::to_value(init)?);
map.insert("start_daemon".into(), serde_json::to_value(start_daemon)?);
Ok(map)
}
/// Which init system to configure
pub fn init(&mut self, init: InitSystem) -> &mut Self {
self.init = init;
self
}
/// Start the daemon (if one is configured)
pub fn start_daemon(&mut self, toggle: bool) -> &mut Self {
self.start_daemon = toggle;
self
}
}
/// An error originating from a [`Planner::settings`](crate::planner::Planner::settings)
#[derive(thiserror::Error, Debug)]
pub enum InstallSettingsError {
@ -369,4 +515,6 @@ pub enum InstallSettingsError {
#[from]
serde_json::Error,
),
#[error("No supported init system found")]
InitNotSupported,
}

View file

@ -546,7 +546,10 @@
"setup_default_profile": {
"action": {
"channels": [
"nixpkgs"
[
"nixpkgs",
"https://nixos.org/channels/nixpkgs-unstable"
]
]
},
"state": "Uncompleted"
@ -632,25 +635,22 @@
"user": null,
"group": null,
"mode": 436,
"buf": "\n\nbuild-users-group = nixbld\n\nexperimental-features = nix-command flakes\n\nauto-optimise-store = true\n",
"buf": "# Generated by https://github.com/DeterminateSystems/nix-installer, version 0.1.0-unreleased.\n\n\n\nbuild-users-group = nixbld\n\nexperimental-features = nix-command flakes\n\nauto-optimise-store = true\n\nbash-prompt-prefix = (nix:$name)\\040\n",
"force": false
},
"state": "Uncompleted"
}
},
"state": "Uncompleted"
},
"configure_nix_daemon_service": {
"action": {},
"state": "Uncompleted"
}
},
"state": "Uncompleted"
},
{
"action": {
"action": "kickstart_launchctl_service",
"unit": "system/org.nixos.nix-daemon"
"action": "configure_nix_daemon",
"init": "Launchd",
"start_daemon": true
},
"state": "Uncompleted"
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,15 @@
use nix_installer::InstallPlan;
#[cfg(target_os = "linux")]
const LINUX_MULTI: &str = include_str!("./fixtures/linux/linux-multi.json");
#[cfg(target_os = "linux")]
const STEAM_DECK: &str = include_str!("./fixtures/linux/steam-deck.json");
#[cfg(target_os = "macos")]
const DARWIN_MULTI: &str = include_str!("./fixtures/darwin/darwin-multi.json");
// Ensure existing plans still parse
// If this breaks and you need to update the fixture, disable these tests, bump `nix_installer` to a new version, and update the plans.
#[cfg(target_os = "linux")]
#[test]
fn plan_compat_linux_multi() -> eyre::Result<()> {
let _: InstallPlan = serde_json::from_str(LINUX_MULTI)?;
@ -14,6 +18,7 @@ fn plan_compat_linux_multi() -> eyre::Result<()> {
// Ensure existing plans still parse
// If this breaks and you need to update the fixture, disable these tests, bump `nix_installer` to a new version, and update the plans.
#[cfg(target_os = "linux")]
#[test]
fn plan_compat_steam_deck() -> eyre::Result<()> {
let _: InstallPlan = serde_json::from_str(STEAM_DECK)?;
@ -22,6 +27,7 @@ fn plan_compat_steam_deck() -> eyre::Result<()> {
// Ensure existing plans still parse
// If this breaks and you need to update the fixture, disable these tests, bump `nix_installer` to a new version, and update the plans.
#[cfg(target_os = "macos")]
#[test]
fn plan_compat_darwin_multi() -> eyre::Result<()> {
let _: InstallPlan = serde_json::from_str(DARWIN_MULTI)?;

View file

@ -0,0 +1,75 @@
param([switch]$Systemd = $false)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# 22.04 https://cloud-images.ubuntu.com/wsl/jammy/current/
$url = "https://cloud-images.ubuntu.com/wsl/jammy/current/ubuntu-jammy-wsl-amd64-wsl.rootfs.tar.gz"
$File = "ubuntu-jammy-wsl-amd64-wsl.rootfs.tar.gz"
$Name = "ubuntu-jammy"
$TemporaryDirectory = "$HOME/nix-installer-wsl-tests-temp"
$Image = "$TemporaryDirectory\$File"
if (!(Test-Path -Path $Image)) {
Write-Output "Fetching $File to $Image..."
New-Item $TemporaryDirectory -ItemType Directory | Out-Null
Invoke-WebRequest -Uri "https://cloud-images.ubuntu.com/wsl/jammy/current/ubuntu-jammy-wsl-amd64-wsl.rootfs.tar.gz" -OutFile $Image
} else {
Write-Output "Found existing $Image..."
}
$DistroName = "nix-installer-test-$Name"
$InstallRoot = "$TemporaryDirectory\wsl-$Name"
Write-Output "Creating WSL distribution $DistroName from $Image at $InstallRoot..."
wsl --import $DistroName $InstallRoot $Image
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
Write-Output "Preparing $DistroName for nix-installer..."
wsl --distribution $DistroName bash --login -c "apt update --quiet"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
wsl --distribution $DistroName bash --login -c "apt install --quiet --yes curl build-essential"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
wsl --distribution $DistroName bash --login -c "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --quiet"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
if ($Systemd) {
$wslConf = "[boot]`nsystemd=true"
New-Item -Path "\\wsl$\$DistroName\etc\wsl.conf" -ItemType "file" -Value $wslConf
wsl --shutdown
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
}
Write-Output "Building and runnings nix-installer in $DistroName..."
Copy-Item -Recurse "$PSScriptRoot\..\.." -Destination "\\wsl$\$DistroName\nix-installer"
$MaybeInitChoice = switch ($Systemd) {
$true { "" }
$false { "--init none" }
}
wsl --distribution $DistroName bash --login -c "/root/.cargo/bin/cargo run --quiet --manifest-path /nix-installer/Cargo.toml -- install linux-multi --no-confirm $MaybeInitChoice"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
Write-Output "Testing installed Nix on $DistroName..."
wsl --distribution $DistroName bash --login -c "nix run nixpkgs#hello"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
Write-Output "Unregistering $DistroName and removing $InstallRoot..."
wsl --unregister $DistroName
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
Remove-Item $InstallRoot