mirror of
https://github.com/DeterminateSystems/nix-installer-action.git
synced 2024-12-23 13:32:06 +01:00
KVM support out of the box, plus a refreshed README (#56)
* support kvm * Refresh the readme * Update README.md Co-authored-by: Ana Hobden <operator@hoverbear.org> * Update README.md Co-authored-by: Luc Perkins <lucperkins@gmail.com> * Update README.md --------- Co-authored-by: Ana Hobden <operator@hoverbear.org> Co-authored-by: Luc Perkins <lucperkins@gmail.com>
This commit is contained in:
parent
5620eb4af6
commit
07b8bcba1b
5 changed files with 240 additions and 15 deletions
62
README.md
62
README.md
|
@ -1,6 +1,19 @@
|
||||||
# Nix Installer Action
|
# The Determinate Nix Installer Action
|
||||||
|
|
||||||
You can use [`nix-installer`](https://github.com/DeterminateSystems/nix-installer) as a Github action like so:
|
Based on the [Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer), responsible for over tens of thousands of Nix installs daily.
|
||||||
|
The fast, friendly, and reliable GitHub Action to install Nix with Flakes.
|
||||||
|
|
||||||
|
## Supports
|
||||||
|
|
||||||
|
* ✅ **Accelerated KVM** on open source projects and larger runners. See [GitHub's announcement](https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/) for more info.
|
||||||
|
* ✅ Linux, x86_64, aarch64, and i686
|
||||||
|
* ✅ macOS, x86_64 and aarch64
|
||||||
|
* ✅ WSL2, x86_64 and aarch64
|
||||||
|
* ✅ Containers
|
||||||
|
* ✅ Valve's SteamOS
|
||||||
|
* ✅ GitHub Hosted, self-hosted, and long running Actions Runners
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
on:
|
on:
|
||||||
|
@ -11,18 +24,16 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
lints:
|
lints:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install Nix
|
- uses: DeterminateSystems/nix-installer-action@main
|
||||||
uses: DeterminateSystems/nix-installer-action@main
|
- run: nix build .
|
||||||
- name: Run `nix build`
|
|
||||||
run: nix build .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See [`.github/workflows/ci.yml`](.github/workflows/ci.yml) for a full example.
|
### With FlakeHub
|
||||||
|
|
||||||
To use private flakes from FlakeHub, use a configuration like this:
|
To fetch private flakes from FlakeHub, update the `permissions` block and pass `flakehub: true`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
on:
|
on:
|
||||||
|
@ -33,20 +44,42 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
lints:
|
lints:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
id-token: "write"
|
id-token: "write"
|
||||||
contents: "read"
|
contents: "read"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install Nix
|
- uses: DeterminateSystems/nix-installer-action@main
|
||||||
uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
with:
|
with:
|
||||||
flakehub: true
|
flakehub: true
|
||||||
- name: Run `nix build`
|
- run: nix build .
|
||||||
run: nix build .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See [`.github/workflows/ci.yml`](.github/workflows/ci.yml) for a full example.
|
||||||
|
|
||||||
|
### Advanced Usage
|
||||||
|
|
||||||
|
* If KVM is available, the installer sets up KVM so that Nix can use it ,and exports the `DETERMINATE_NIX_KVM` environment variable set to 1.
|
||||||
|
If KVM is not available, `DETERMINATE_NIX_KVM` is set to 0.
|
||||||
|
This can be used in combination with GitHub Actions' `if` syntax for turning on and off steps.
|
||||||
|
|
||||||
|
## Installation Differences
|
||||||
|
|
||||||
|
Differing from the upstream [Nix](https://github.com/NixOS/nix) installer scripts:
|
||||||
|
|
||||||
|
* In `nix.conf`:
|
||||||
|
+ the `nix-command` and `flakes` features are enabled
|
||||||
|
+ `bash-prompt-prefix` is set
|
||||||
|
+ `auto-optimise-store` is set to `true` (On Linux only)
|
||||||
|
* `extra-nix-path` is set to `nixpkgs=flake:nixpkgs`
|
||||||
|
* `max-jobs` is set to `auto`
|
||||||
|
* KVM is enabled by default.
|
||||||
|
* an installation receipt (for uninstalling) is stored at `/nix/receipt.json` as well as a copy of the install binary at `/nix/nix-installer`
|
||||||
|
* `nix-channel --update` is not run, `~/.nix-channels` is not provisioned
|
||||||
|
* `ssl-cert-file` is set in `/etc/nix/nix.conf` if the `ssl-cert-file` argument is used.
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
| Parameter | Description | Type | Default |
|
| Parameter | Description | Type | Default |
|
||||||
|
@ -57,6 +90,7 @@ jobs:
|
||||||
| `flakehub` | Log in to FlakeHub to pull private flakes using the GitHub Actions [JSON Web Token](https://jwt.io) (JWT), which is bound to the `api.flakehub.com` audience. | Boolean | `false` |
|
| `flakehub` | Log in to FlakeHub to pull private flakes using the GitHub Actions [JSON Web Token](https://jwt.io) (JWT), which is bound to the `api.flakehub.com` audience. | Boolean | `false` |
|
||||||
| `github-token` | A [GitHub token] for making authenticated requests (which have a higher rate-limit quota than unauthenticated requests) | string | `${{ github.token }}` |
|
| `github-token` | A [GitHub token] for making authenticated requests (which have a higher rate-limit quota than unauthenticated requests) | string | `${{ github.token }}` |
|
||||||
| `init` | The init system to configure (requires `planner: linux-multi`) | enum (`none` or `systemd`) | |
|
| `init` | The init system to configure (requires `planner: linux-multi`) | enum (`none` or `systemd`) | |
|
||||||
|
| `kvm` | Automatically configure the GitHub Actions Runner for NixOS test support, if the host supports it. | Boolean | `true` |
|
||||||
| `local-root` | A local `nix-installer` binary root. Overrides the `nix-installer-url` setting (a `nix-installer.sh` should exist, binaries should be named `nix-installer-$ARCH`, eg. `nix-installer-x86_64-linux`). | Boolean | `false` |
|
| `local-root` | A local `nix-installer` binary root. Overrides the `nix-installer-url` setting (a `nix-installer.sh` should exist, binaries should be named `nix-installer-$ARCH`, eg. `nix-installer-x86_64-linux`). | Boolean | `false` |
|
||||||
| `log-directives` | A list of [tracing directives], comma separated with `-`s replaced with `_` (eg. `nix_installer=trace`) | string | |
|
| `log-directives` | A list of [tracing directives], comma separated with `-`s replaced with `_` (eg. `nix_installer=trace`) | string | |
|
||||||
| `logger` | The logger to use during installation | enum (`pretty`, `json`, `full`, `compact`) | |
|
| `logger` | The logger to use during installation | enum (`pretty`, `json`, `full`, `compact`) | |
|
||||||
|
|
|
@ -23,6 +23,10 @@ inputs:
|
||||||
init:
|
init:
|
||||||
description: "The init system to configure, requires `planner: linux-multi` (allowing the choice between `none` or `systemd`)"
|
description: "The init system to configure, requires `planner: linux-multi` (allowing the choice between `none` or `systemd`)"
|
||||||
required: false
|
required: false
|
||||||
|
kvm:
|
||||||
|
description: Automatically configure the GitHub Actions Runner for NixOS test supports, if the host supports it.
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
local-root:
|
local-root:
|
||||||
description: A local `nix-installer` binary root, overrides any settings which change the `nix-installer` used (binaries should be named `nix-installer-$ARCH-$OS`, eg. `nix-installer-x86_64-linux`)
|
description: A local `nix-installer` binary root, overrides any settings which change the `nix-installer` used (binaries should be named `nix-installer-$ARCH-$OS`, eg. `nix-installer-x86_64-linux`)
|
||||||
required: false
|
required: false
|
||||||
|
|
81
dist/index.js
vendored
81
dist/index.js
vendored
|
@ -41,6 +41,7 @@ class NixInstallerAction {
|
||||||
this.extra_args = action_input_string_or_null("extra-args");
|
this.extra_args = action_input_string_or_null("extra-args");
|
||||||
this.extra_conf = action_input_multiline_string_or_null("extra-conf");
|
this.extra_conf = action_input_multiline_string_or_null("extra-conf");
|
||||||
this.flakehub = action_input_bool("flakehub");
|
this.flakehub = action_input_bool("flakehub");
|
||||||
|
this.kvm = action_input_bool("kvm");
|
||||||
this.github_token = action_input_string_or_null("github-token");
|
this.github_token = action_input_string_or_null("github-token");
|
||||||
this.init = action_input_string_or_null("init");
|
this.init = action_input_string_or_null("init");
|
||||||
this.local_root = action_input_string_or_null("local-root");
|
this.local_root = action_input_string_or_null("local-root");
|
||||||
|
@ -236,6 +237,20 @@ class NixInstallerAction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.kvm) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.startGroup("Configuring KVM");
|
||||||
|
if (await this.setup_kvm()) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.endGroup();
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.info("\u001b[32m Accelerated KVM is enabled \u001b[33m⚡️");
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.exportVariable("DETERMINATE_NIX_KVM", "1");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.endGroup();
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.info("KVM is not available.");
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.exportVariable("DETERMINATE_NIX_KVM", "0");
|
||||||
|
}
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.exportVariable("DETERMINATE_NIX_KVM", "0");
|
||||||
|
}
|
||||||
// Normal just doing of the install
|
// Normal just doing of the install
|
||||||
const binary_path = await this.fetch_binary();
|
const binary_path = await this.fetch_binary();
|
||||||
await this.execute_install(binary_path);
|
await this.execute_install(binary_path);
|
||||||
|
@ -305,6 +320,72 @@ class NixInstallerAction {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async setup_kvm() {
|
||||||
|
const kvm_rules = "/etc/udev/rules.d/99-determinate-nix-installer-kvm.rules";
|
||||||
|
try {
|
||||||
|
const write_file_exit_code = await _actions_exec__WEBPACK_IMPORTED_MODULE_3__.exec("sh", [
|
||||||
|
"-c",
|
||||||
|
`echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee ${kvm_rules} > /dev/null`,
|
||||||
|
], {
|
||||||
|
listeners: {
|
||||||
|
stderr: (data) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (write_file_exit_code !== 0) {
|
||||||
|
throw new Error(`Non-zero exit code of \`${write_file_exit_code}\` detected while writing '${kvm_rules}'`);
|
||||||
|
}
|
||||||
|
const debug_run_throw = async (action, command, args) => {
|
||||||
|
const reload_exit_code = await _actions_exec__WEBPACK_IMPORTED_MODULE_3__.exec(command, args, {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stderr: (data) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (reload_exit_code !== 0) {
|
||||||
|
throw new Error(`Non-zero exit code of \`${reload_exit_code}\` detected while ${action}.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await debug_run_throw("reloading udev rules", `sudo`, [
|
||||||
|
"udevadm",
|
||||||
|
"control",
|
||||||
|
"--reload-rules",
|
||||||
|
]);
|
||||||
|
await debug_run_throw("triggering udev against kvm", `sudo`, [
|
||||||
|
"udevadm",
|
||||||
|
"trigger",
|
||||||
|
"--name-match=kvm",
|
||||||
|
]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
await _actions_exec__WEBPACK_IMPORTED_MODULE_3__.exec("sudo", ["rm", "-f", kvm_rules], {
|
||||||
|
listeners: {
|
||||||
|
stderr: (data) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.info(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
async fetch_binary() {
|
async fetch_binary() {
|
||||||
if (!this.local_root) {
|
if (!this.local_root) {
|
||||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__.info(`Fetching binary from ${this.nix_installer_url}`);
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__.info(`Fetching binary from ${this.nix_installer_url}`);
|
||||||
|
|
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
106
src/main.ts
106
src/main.ts
|
@ -15,6 +15,7 @@ class NixInstallerAction {
|
||||||
extra_args: string | null;
|
extra_args: string | null;
|
||||||
extra_conf: string[] | null;
|
extra_conf: string[] | null;
|
||||||
flakehub: boolean;
|
flakehub: boolean;
|
||||||
|
kvm: boolean;
|
||||||
github_token: string | null;
|
github_token: string | null;
|
||||||
// TODO: linux_init
|
// TODO: linux_init
|
||||||
init: string | null;
|
init: string | null;
|
||||||
|
@ -53,6 +54,7 @@ class NixInstallerAction {
|
||||||
this.extra_args = action_input_string_or_null("extra-args");
|
this.extra_args = action_input_string_or_null("extra-args");
|
||||||
this.extra_conf = action_input_multiline_string_or_null("extra-conf");
|
this.extra_conf = action_input_multiline_string_or_null("extra-conf");
|
||||||
this.flakehub = action_input_bool("flakehub");
|
this.flakehub = action_input_bool("flakehub");
|
||||||
|
this.kvm = action_input_bool("kvm");
|
||||||
this.github_token = action_input_string_or_null("github-token");
|
this.github_token = action_input_string_or_null("github-token");
|
||||||
this.init = action_input_string_or_null("init");
|
this.init = action_input_string_or_null("init");
|
||||||
this.local_root = action_input_string_or_null("local-root");
|
this.local_root = action_input_string_or_null("local-root");
|
||||||
|
@ -305,6 +307,24 @@ class NixInstallerAction {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.kvm) {
|
||||||
|
actions_core.startGroup("Configuring KVM");
|
||||||
|
if (await this.setup_kvm()) {
|
||||||
|
actions_core.endGroup();
|
||||||
|
actions_core.info(
|
||||||
|
"\u001b[32m Accelerated KVM is enabled \u001b[33m⚡️",
|
||||||
|
);
|
||||||
|
actions_core.exportVariable("DETERMINATE_NIX_KVM", "1");
|
||||||
|
} else {
|
||||||
|
actions_core.endGroup();
|
||||||
|
actions_core.info("KVM is not available.");
|
||||||
|
actions_core.exportVariable("DETERMINATE_NIX_KVM", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
actions_core.exportVariable("DETERMINATE_NIX_KVM", "0");
|
||||||
|
}
|
||||||
|
|
||||||
// Normal just doing of the install
|
// Normal just doing of the install
|
||||||
const binary_path = await this.fetch_binary();
|
const binary_path = await this.fetch_binary();
|
||||||
await this.execute_install(binary_path);
|
await this.execute_install(binary_path);
|
||||||
|
@ -399,6 +419,92 @@ class NixInstallerAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async setup_kvm(): Promise<boolean> {
|
||||||
|
const kvm_rules =
|
||||||
|
"/etc/udev/rules.d/99-determinate-nix-installer-kvm.rules";
|
||||||
|
try {
|
||||||
|
const write_file_exit_code = await actions_exec.exec(
|
||||||
|
"sh",
|
||||||
|
[
|
||||||
|
"-c",
|
||||||
|
`echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee ${kvm_rules} > /dev/null`,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
listeners: {
|
||||||
|
stderr: (data: Buffer) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
actions_core.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (write_file_exit_code !== 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Non-zero exit code of \`${write_file_exit_code}\` detected while writing '${kvm_rules}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const debug_run_throw = async (
|
||||||
|
action: string,
|
||||||
|
command: string,
|
||||||
|
args: string[],
|
||||||
|
): Promise<void> => {
|
||||||
|
const reload_exit_code = await actions_exec.exec(command, args, {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
actions_core.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stderr: (data: Buffer) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
actions_core.debug(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reload_exit_code !== 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Non-zero exit code of \`${reload_exit_code}\` detected while ${action}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await debug_run_throw("reloading udev rules", `sudo`, [
|
||||||
|
"udevadm",
|
||||||
|
"control",
|
||||||
|
"--reload-rules",
|
||||||
|
]);
|
||||||
|
|
||||||
|
await debug_run_throw("triggering udev against kvm", `sudo`, [
|
||||||
|
"udevadm",
|
||||||
|
"trigger",
|
||||||
|
"--name-match=kvm",
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
await actions_exec.exec("sudo", ["rm", "-f", kvm_rules], {
|
||||||
|
listeners: {
|
||||||
|
stderr: (data: Buffer) => {
|
||||||
|
const trimmed = data.toString("utf-8").trimEnd();
|
||||||
|
if (trimmed.length >= 0) {
|
||||||
|
actions_core.info(trimmed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async fetch_binary(): Promise<string> {
|
private async fetch_binary(): Promise<string> {
|
||||||
if (!this.local_root) {
|
if (!this.local_root) {
|
||||||
actions_core.info(`Fetching binary from ${this.nix_installer_url}`);
|
actions_core.info(`Fetching binary from ${this.nix_installer_url}`);
|
||||||
|
|
Loading…
Reference in a new issue