mirror of
https://github.com/NiklasGollenstede/nixos-installer.git
synced 2024-11-21 15:33:21 +01:00
many small fixes/improvements, and:
add config.wip.fs.temproot.remote.type == none, add config.wip.base.includeInputs, add pkgs.uboot-with-mmc-env, rename config.preface to config.wip.preface
This commit is contained in:
parent
b51cf19f4a
commit
8b0e200c73
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -80,6 +80,7 @@
|
||||
"keystatus", // zfs
|
||||
"kmod", // linux
|
||||
"lazytime", // f2fs
|
||||
"libblockdev", // package
|
||||
"libubootenv", // package
|
||||
"logbias", // zfs
|
||||
"losetup", // program / function
|
||||
@ -88,6 +89,7 @@
|
||||
"luks", // linux
|
||||
"macaddr", // cli arg
|
||||
"mkdir", // program / function
|
||||
"mkenvimage", // program
|
||||
"mktemp", // program / function
|
||||
"modifyvm", // virtual box
|
||||
"mountpoint", // program / function
|
||||
@ -109,10 +111,12 @@
|
||||
"noheadings", // cli arg
|
||||
"nosuid", // mount option
|
||||
"oneshot", // systemd
|
||||
"optimise", // B/E
|
||||
"ostype", // virtual box
|
||||
"OVMF", // package
|
||||
"partlabel", // linux
|
||||
"partprobe", // program / function
|
||||
"passthru", // nix
|
||||
"pbkdf", // cli arg
|
||||
"pflash", // cli arg
|
||||
"pkgs", // nix
|
||||
@ -155,6 +159,7 @@
|
||||
"typecode", // cli arg
|
||||
"uart", "uarts", // serial protocol
|
||||
"uartmode", // virtual box
|
||||
"uboot", // program
|
||||
"udev", // program
|
||||
"udevadm", // program
|
||||
"udptunnel", // program
|
||||
|
@ -25,7 +25,7 @@ The modules are inactive by default, and are, where possible, designed to be ind
|
||||
[`patches/`](./patches/) contains patches which are either applied to the flake's inputs in [`flake.nix`](./flake.nix) or to packages in one of the [`overlays/`](./overlays/).
|
||||
|
||||
[`hosts/`](./hosts/) contains the main NixOS config modules for each host. Generally, there is one file for each host, but the [flake](./flake.nix) can be instructed to reuse the config for multiple hosts (in which case the module should probably interpret the `name` argument passed to it).
|
||||
Any `preface.*` options have to be set in the first sub-module in these files (`## Hardware` section). \
|
||||
Any `wip.preface.*` options have to be set in the first sub-module in these files (`## Hardware` section). \
|
||||
This flake only defines a single [`example`](./hosts/example.nix.md) host meant to demonstrate how other flakes can use the (NixOS) flake library framework.
|
||||
|
||||
[`example/`](./example/) contains an example of adjusting the [installation](./example/install.sh.md) script for the hosts and this flake's [default config](./example/defaultConfig/) (see [Namespacing](#namespacing-in-nixos)).
|
||||
|
BIN
flake.lock
BIN
flake.lock
Binary file not shown.
@ -17,10 +17,10 @@
|
||||
# ./patches/nixpkgs-fix-systemd-boot-install.patch
|
||||
];
|
||||
|
||||
}; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@ { self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let
|
||||
}; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@{ self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let
|
||||
|
||||
systemsFlake = lib.wip.mkSystemsFlake (rec {
|
||||
#systems = { dir = "${inputs.self}/hosts"; exclude = [ ]; }; # (implicit)
|
||||
#systems = { dir = "${self}/hosts"; exclude = [ ]; }; # (implicit)
|
||||
inherit inputs;
|
||||
scripts = (lib.attrValues lib.wip.setup-scripts) ++ [ ./example/install.sh.md ];
|
||||
});
|
||||
@ -29,8 +29,8 @@ in [ # Run »nix flake show --allow-import-from-derivation« to see what this me
|
||||
repo
|
||||
(if true then systemsFlake else { })
|
||||
(lib.wip.forEachSystem [ "aarch64-linux" "x86_64-linux" ] (localSystem: {
|
||||
packages = lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays;
|
||||
packages = builtins.removeAttrs (lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays) [ "libblockdev" ];
|
||||
defaultPackage = systemsFlake.packages.${localSystem}.all-systems;
|
||||
}))
|
||||
{ patches = import "${inputs.self}/patches" "${inputs.self}/patches" inputs; }
|
||||
{ patches = (lib.wip.importWrapped inputs "${self}/patches").result; }
|
||||
]); }
|
||||
|
@ -28,12 +28,12 @@ Alternative to running directly as `root` (esp. if `nix` is not installed for ro
|
||||
```nix
|
||||
#*/# end of MarkDown, beginning of NixOS config flake input:
|
||||
dirname: inputs: { config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let
|
||||
#suffix = builtins.head (builtins.match ''example-(.*)'' name); # make differences in config based on this when using »preface.instances«
|
||||
#suffix = builtins.head (builtins.match ''example-(.*)'' name); # make differences in config based on this when using »wip.preface.instances«
|
||||
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
|
||||
in { imports = [ ({ ## Hardware
|
||||
preface.instances = [ "example" "example-raidz" ];
|
||||
wip.preface.instances = [ "example" "example-minimal" "example-raidz" ];
|
||||
|
||||
preface.hardware = "x86_64"; system.stateVersion = "22.05";
|
||||
wip.preface.hardware = "x86_64"; system.stateVersion = "22.05";
|
||||
|
||||
## What follows is a whole bunch of boilerplate-ish stuff, most of which multiple hosts would have in common and which would thus be moved to one or more modules:
|
||||
|
||||
@ -81,6 +81,16 @@ in { imports = [ ({ ## Hardware
|
||||
wip.fs.temproot.local.mounts."/var/log" = lib.mkForce null; # example: don't keep logs
|
||||
|
||||
|
||||
}) (lib.mkIf (name == "example-minimal") { ## Minimal automatic FS setup
|
||||
|
||||
wip.fs.boot.enable = true;
|
||||
wip.fs.temproot.enable = true;
|
||||
wip.fs.temproot.temp.type = "tmpfs";
|
||||
wip.fs.temproot.local.type = "bind";
|
||||
wip.fs.temproot.local.bind.base = "f2fs";
|
||||
wip.fs.temproot.remote.type = "none";
|
||||
|
||||
|
||||
}) (lib.mkIf (name == "example-raidz") { ## Multi-disk ZFS setup
|
||||
|
||||
#wip.fs.disks.devices.primary.size = "16G"; # (default)
|
||||
@ -104,7 +114,7 @@ in { imports = [ ({ ## Hardware
|
||||
}) ({ ## Actual Config
|
||||
|
||||
# Some base config:
|
||||
wip.base.enable = true; wip.base.includeNixpkgs = inputs.nixpkgs;
|
||||
wip.base.enable = true;
|
||||
documentation.enable = false; # sometimes takes quite long to build
|
||||
|
||||
|
||||
|
@ -3,4 +3,4 @@ dirname: inputs@{ self, nixpkgs, ...}: let
|
||||
#categories = fix (wip: (import "${dirname}/imports.nix" dirname inputs).importAll (inputs // { self = inputs.self // { lib = nixpkgs.lib // { inherit wip; }; }; })) dirname;
|
||||
categories = (import "${dirname}/imports.nix" dirname inputs).importAll inputs dirname;
|
||||
wip = (builtins.foldl' (a: b: a // b) { } (builtins.attrValues (builtins.removeAttrs categories [ "setup-scripts" ]))) // categories;
|
||||
in nixpkgs.lib // { inherit wip; }
|
||||
in nixpkgs.lib // { wip = wip // { prefix = inputs.config.prefix; }; }
|
||||
|
@ -3,6 +3,8 @@ dirname: inputs@{ self, nixpkgs, ...}: let
|
||||
inherit (import "${dirname}/vars.nix" dirname inputs) mapMerge mergeAttrsUnique flipNames;
|
||||
inherit (import "${dirname}/imports.nix" dirname inputs) getModifiedPackages getNixFiles importWrapped;
|
||||
inherit (import "${dirname}/scripts.nix" dirname inputs) substituteImplicit;
|
||||
setup-scripts = (import "${dirname}/setup-scripts" "${dirname}/setup-scripts" inputs);
|
||||
prefix = inputs.config.prefix;
|
||||
in rec {
|
||||
|
||||
# Simplified implementation of »flake-utils.lib.eachSystem«.
|
||||
@ -10,7 +12,7 @@ in rec {
|
||||
|
||||
# Sooner or later this should be implemented in nix itself, for now require »inputs.nixpkgs« and a system that can run »x86_64-linux« (native or through qemu).
|
||||
patchFlakeInputs = inputs: patches: outputs: let
|
||||
inherit ((import inputs.nixpkgs { system = "x86_64-linux"; }).pkgs) applyPatches fetchpatch;
|
||||
inherit ((import inputs.nixpkgs { overlays = [ ]; config = { }; system = "x86_64-linux"; }).pkgs) applyPatches fetchpatch;
|
||||
in outputs (builtins.mapAttrs (name: input: if name != "self" && patches?${name} && patches.${name} != [ ] then (let
|
||||
patched = applyPatches {
|
||||
name = "${name}-patched"; src = input;
|
||||
@ -26,22 +28,31 @@ in rec {
|
||||
in { inherit (input) inputs; inherit outputs; inherit sourceInfo; } // outputs // sourceInfo)
|
||||
)) else input) inputs);
|
||||
|
||||
# Generates implicit flake outputs by importing conventional paths in the local repo.
|
||||
# Generates implicit flake outputs by importing conventional paths in the local repo. E.g.:
|
||||
# outputs = inputs@{ self, nixpkgs, wiplib, ... }: wiplib.lib.wip.importRepo inputs ./. (repo@{ overlays, lib, ... }: let ... in [ repo ... ])
|
||||
importRepo = inputs: repoPath': outputs: let
|
||||
repoPath = builtins.path { path = repoPath'; name = "source"; }; # referring to the current flake directory as »./.« is quite intuitive (and »inputs.self.outPath« causes infinite recursion), but without this it adds another hash to the path (because it copies it)
|
||||
in let list = (outputs inputs ((if builtins.pathExists "${repoPath}/lib/default.nix" then {
|
||||
lib = import "${repoPath}/lib" "${repoPath}/lib" inputs;
|
||||
} else { }) // (if builtins.pathExists "${repoPath}/overlays/default.nix" then rec {
|
||||
overlays = import "${repoPath}/overlays" "${repoPath}/overlays" inputs;
|
||||
overlay = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues overlays);
|
||||
} else { }) // (if builtins.pathExists "${repoPath}/modules/default.nix" then rec {
|
||||
nixosModules = import "${repoPath}/modules" "${repoPath}/modules" inputs;
|
||||
nixosModule = { imports = builtins.attrValues nixosModules; };
|
||||
} else { }))); in if (builtins.isList list) then mergeOutputs list else list;
|
||||
in let result = (outputs (
|
||||
(let it = importWrapped inputs "${repoPath}/lib"; in if it.exists then rec {
|
||||
lib = it.result;
|
||||
} else { }) // (let it = importWrapped inputs "${repoPath}/overlays"; in if it.exists then rec {
|
||||
overlays = it.result;
|
||||
overlay = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues overlays);
|
||||
} else { }) // (let it = importWrapped inputs "${repoPath}/modules"; in if it.exists then rec {
|
||||
nixosModules = it.result;
|
||||
nixosModule = { imports = builtins.attrValues nixosModules; };
|
||||
} else { })
|
||||
)); in if (builtins.isList result) then mergeOutputs result else result;
|
||||
|
||||
# Combines »patchFlakeInputs« and »importRepo« in a single call.
|
||||
# Combines »patchFlakeInputs« and »importRepo« in a single call. E.g.:
|
||||
# outputs = inputs: let patches = {
|
||||
# nixpkgs = [
|
||||
# # remote: { url = "https://github.com/NixOS/nixpkgs/pull/###.diff"; sha256 = inputs.nixpkgs.lib.fakeSha256; }
|
||||
# # local: ./overlays/patches/nixpkgs-###.patch # (use long native path to having the path change if any of the other files in ./. change)
|
||||
# ]; # ...
|
||||
# }; in inputs.wiplib.lib.wip.patchFlakeInputsAndImportRepo inputs patches ./. (inputs@{ self, nixpkgs, ... }: repo@{ nixosModules, overlays, lib, ... }: let ... in [ repo ... ])
|
||||
patchFlakeInputsAndImportRepo = inputs: patches: repoPath: outputs: (
|
||||
patchFlakeInputs inputs patches (inputs: importRepo inputs repoPath outputs)
|
||||
patchFlakeInputs inputs patches (inputs: importRepo inputs repoPath (outputs inputs))
|
||||
);
|
||||
|
||||
# Merges a list of flake output attribute sets.
|
||||
@ -53,11 +64,11 @@ in rec {
|
||||
|
||||
# Given a path to a host config file, returns some properties defined in its first inline module (to be used where accessing them via »nodes.${name}.config...« isn't possible).
|
||||
getSystemPreface = inputs: entryPath: args: let
|
||||
imported = (importWrapped inputs entryPath) ({ config = null; pkgs = null; lib = null; name = null; nodes = null; } // args);
|
||||
module = builtins.elemAt imported.imports 0; props = module.preface;
|
||||
imported = (importWrapped inputs entryPath).required ({ config = null; pkgs = null; lib = null; name = null; nodes = null; } // args);
|
||||
module = builtins.elemAt imported.imports 0; props = module.${prefix}.preface;
|
||||
in if (
|
||||
imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?preface && props?hardware
|
||||
) then (props) else throw "File ${entryPath} must fulfill the structure: dirname: inputs: { ... }: { imports = [ { preface = { hardware = str; ... } } ]; }";
|
||||
imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?${prefix} && module.${prefix}?preface && props?hardware
|
||||
) then (props) else throw "File ${entryPath} must fulfill the structure: dirname: inputs: { ... }: { imports = [ { ${prefix}.preface = { hardware = str; ... } } ]; }";
|
||||
|
||||
# Builds the System Configuration for a single host. Since each host depends on the context of all other host (in the same "network"), this is essentially only callable through »mkNixosConfigurations«.
|
||||
# See »mkSystemsFlake« for documentation of the arguments.
|
||||
@ -72,13 +83,13 @@ in rec {
|
||||
in { inherit preface; } // (nixosSystem {
|
||||
system = targetSystem;
|
||||
modules = [ (
|
||||
{ _file = entryPath; imports = [ (importWrapped inputs entryPath) ]; } # (preserve the location of reported errors)
|
||||
(importWrapped inputs entryPath).module
|
||||
) {
|
||||
# The system architecture (often referred to as »system«).
|
||||
options.preface.hardware = lib.mkOption { type = lib.types.str; readOnly = true; };
|
||||
options.${prefix}.preface.hardware = lib.mkOption { type = lib.types.str; readOnly = true; };
|
||||
} {
|
||||
# List of host names to instantiate this host config for, instead of just for the file name.
|
||||
options.preface.instances = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ name ]; };
|
||||
options.${prefix}.preface.instances = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ name ]; };
|
||||
} ({ config, ... }: {
|
||||
|
||||
imports = modules; nixpkgs = { inherit overlays; }
|
||||
@ -95,13 +106,13 @@ in rec {
|
||||
});
|
||||
|
||||
# Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of file names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided.
|
||||
# If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »preface.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »specialArgs« to the modules and can thus be used to adjust the config per instance).
|
||||
# If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »${prefix}.preface.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »specialArgs« to the modules and can thus be used to adjust the config per instance).
|
||||
# All other arguments are as specified by »mkSystemsFlake« and are passed to »mkNixosConfiguration«.
|
||||
mkNixosConfigurations = args: let # { files, dir, exclude, ... }
|
||||
files = args.files or (getNixFiles args.dir (args.exclude or [ ]));
|
||||
files' = if builtins.isAttrs files then files else (builtins.listToAttrs (map (entryPath: let
|
||||
stripped = builtins.match ''^(.*)[.]nix[.]md$'' (builtins.baseNameOf entryPath);
|
||||
name = if stripped != null then (builtins.elemAt stripped 0) else (builtins.baseNameOf entryPath);
|
||||
name = builtins.unsafeDiscardStringContext (if stripped != null then (builtins.elemAt stripped 0) else (builtins.baseNameOf entryPath));
|
||||
in { inherit name; value = entryPath; }) files));
|
||||
|
||||
configs = mapMerge (name: entryPath: (let
|
||||
@ -118,7 +129,7 @@ in rec {
|
||||
ids = mapMerge (name: node: { "${toString node.preface.id}" = name; }) withId;
|
||||
duplicate = builtins.removeAttrs withId (builtins.attrValues ids);
|
||||
in if duplicate != { } then (
|
||||
throw "»my.system.id«s are not unique! The following hosts share their IDs with some other host: ${builtins.concatStringsSep ", " (builtins.attrNames duplicate)}"
|
||||
throw "»${prefix}.preface.id«s are not unique! The following hosts share their IDs with some other host: ${builtins.concatStringsSep ", " (builtins.attrNames duplicate)}"
|
||||
) else configs;
|
||||
|
||||
# Builds a system of NixOS hosts and exports them plus managing functions as flake outputs.
|
||||
@ -129,7 +140,7 @@ in rec {
|
||||
# Root path of the NixOS configuration. »./.« in the »flake.nix«
|
||||
configPath ? inputs.self.outPath,
|
||||
# Arguments »{ files, dir, exclude, }« to »mkNixosConfigurations«, see there for details. May also be a list of those attrsets, in which case those multiple sets of hosts will be built separately by »mkNixosConfigurations«, allowing for separate sets of »peers« passed to »mkNixosConfiguration«. Each call will receive all other arguments, and the resulting sets of hosts will be merged.
|
||||
systems ? ({ dir = "${configPath}/hosts/"; exclude = [ ]; }),
|
||||
systems ? ({ dir = "${configPath}/hosts"; exclude = [ ]; }),
|
||||
# List of overlays to set as »config.nixpkgs.overlays«. Defaults to the ».overlay(s)« of all »overlayInputs«/»inputs« (incl. »inputs.self«).
|
||||
overlays ? (builtins.concatLists (map (input: if input?overlay then [ input.overlay ] else if input?overlays then builtins.attrValues input.overlays else [ ]) (builtins.attrValues overlayInputs))),
|
||||
# (Subset of) »inputs« that »overlays« will be used from. Example: »{ inherit (inputs) self flakeA flakeB; }«.
|
||||
@ -140,11 +151,11 @@ in rec {
|
||||
moduleInputs ? (builtins.removeAttrs inputs [ "nixpkgs" ]),
|
||||
# Additional arguments passed to each module evaluated for the host config (if that module is defined as a function).
|
||||
specialArgs ? { },
|
||||
# List of bash scripts defining functions that do installation and maintenance operations. See »apps« below for more information.
|
||||
scripts ? [ ],
|
||||
# The function of that name as defined in »<nixpkgs>/flake.nix«, or equivalent.
|
||||
# Optional list of bash scripts defining functions that do installation and maintenance operations. See »./setup-scripts/README.md« below for more information. To define additional functions (or overwrite individual default ones), use »(lib.attrValues lib.wip.setup-scripts) ++ [ ]« and place any extra scripts in the array.
|
||||
scripts ? (lib.attrValues setup-scripts),
|
||||
# The »nixosSystem« function defined in »<nixpkgs>/flake.nix«, or equivalent.
|
||||
nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
|
||||
# If provided, then cross compilation is enabled for all hosts whose target architecture is different from this. Since cross compilation currently fails for (some stuff in) NixOS, better don't set »localSystem«. Without it, building for other platforms works fine (just slowly) if »boot.binfmt.emulatedSystems« is configured on the building system for the respective target(s).
|
||||
# If provided, then cross compilation is enabled for all hosts whose target architecture is different from this. Since cross compilation currently fails for (some stuff in) NixOS, better don't set »localSystem«. Without it, building for other platforms works fine (just slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s).
|
||||
localSystem ? null,
|
||||
... }: let
|
||||
otherArgs = (builtins.removeAttrs args [ "systems" ]) // { inherit systems overlays modules specialArgs scripts inputs configPath nixosSystem localSystem; };
|
||||
@ -155,7 +166,7 @@ in rec {
|
||||
pkgs = (import inputs.nixpkgs { inherit overlays; system = localSystem; });
|
||||
nix = if lib.versionOlder pkgs.nix.version "2.4" then pkgs.nix_2_4 else pkgs.nix;
|
||||
nix_wrapped = pkgs.writeShellScriptBin "nix" ''exec ${nix}/bin/nix --extra-experimental-features nix-command "$@"'';
|
||||
in (if scripts == [ ] then { } else {
|
||||
in (if scripts == [ ] || scripts == null then { } else {
|
||||
|
||||
# E.g.: $ nix run .#$target -- install-system /tmp/system-$target.img
|
||||
# E.g.: $ nix run /etc/nixos/#$(hostname) -- sudo
|
||||
|
@ -28,11 +28,28 @@ in rec {
|
||||
# Any function with two (usually unnamed) arguments returning an attrset could be an overlay, so that's rather vague.
|
||||
couldBeOverlay = thing: let result1 = thing (builtins.functionArgs thing); result2 = result1 (builtins.functionArgs result1); in builtins.isFunction thing && builtins.isFunction result1 && builtins.isAttrs result2;
|
||||
|
||||
# Builds an attrset that, for each folder or ».nix« or ».nix.md« file (other than »default.nix«) in this folder, as the name of that folder or the name of the file without extension(s), exports the result of importing that file/folder.
|
||||
# Builds an attrset that, for each folder (containing a »default.nix«) or ».nix« or ».nix.md« file (other than »./default.nix«) in this folder, as the name of that folder or the name of the file without extension(s), exports the result of importing that file/folder.
|
||||
importAll = inputs: dir: builtins.mapAttrs (name: path: import path (if endsWith "/default.nix" path then "${dir}/${name}" else dir) inputs) (getNamedNixFiles dir [ "default.nix" ]);
|
||||
|
||||
# Import a Nix file that expects the standard `dirname: inputs: ` arguments.
|
||||
importWrapped = inputs: path: import path (if (builtins.match ''^(.*)[.]nix([.]md)?$'' path) != null then builtins.dirOf path else path) inputs;
|
||||
# Import a Nix file that expects the standard `dirname: inputs: ` arguments, providing some additional information and error handling.
|
||||
importWrapped = inputs: path: rec {
|
||||
# Whether the file is imported by an explicit full path (or one omitting ».nix« or »/default.nix«):
|
||||
isExplicit = (builtins.match ''^(.*)[.]nix([.]md)?$'' path) != null;
|
||||
# Whether the import path _implicitly_ refers to the »/default.nix« in a directory:
|
||||
isImplicitDir = !isExplicit && builtins.pathExists "${path}/default.nix";
|
||||
# The resolved path that will be imported:
|
||||
fullPath = if isImplicitDir then "${path}/default.nix" else if isExplicit then path else "${path}.nix";
|
||||
# The imported nix value:
|
||||
result = import fullPath (if isImplicitDir then path else builtins.dirOf path) inputs;
|
||||
# Whether the import path points to an existing file:
|
||||
exists = isImplicitDir || (builtins.pathExists (if isExplicit then path else "${path}.nix"));
|
||||
# Return »null« if not ».exists«:
|
||||
optional = if exists then result else null;
|
||||
# Throw if not ».exists«:
|
||||
required = if exists then result else throw (if isExplicit then "File ${path} does not exist" else "Neither ${path}/default.nix nor ${path}.nix exist");
|
||||
# ».result« interpreted as NixOS module, wrapped to preserve the import path:
|
||||
module = { _file = fullPath; imports = [ required ]; };
|
||||
};
|
||||
|
||||
## Returns an attrset that, for each file in »dir« (except »default.nix« and as filtered and named by »getNamedNixFiles dir except«), imports that file and exposes only if the result passes »filter«. If provided, the imported value is »wrapped« after filtering.
|
||||
# If a file/folder' import that is rejected by »filter« is an attrset (for example because it results from a call to this function), then all attributes whose values pass »filter« are prefixed with the file/folders name plus a slash and merged into the overall attrset.
|
||||
@ -72,7 +89,7 @@ in rec {
|
||||
# Given a list of »overlays« and »pkgs« with them applied, returns the subset of »pkgs« that was directly modified by the overlays.
|
||||
getModifiedPackages = pkgs: overlays: let
|
||||
names = builtins.concatLists (map (overlay: builtins.attrNames (overlay { } { })) (builtins.attrValues overlays));
|
||||
in mapMerge (name: { ${name} = pkgs.${name}; }) names;
|
||||
in mapMerge (name: if lib.isDerivation pkgs.${name} then { ${name} = pkgs.${name}; } else { }) names;
|
||||
|
||||
## Given a path to a module in »nixpkgs/nixos/modules/«, when placed in another module's »imports«, this adds an option »disableModule.${modulePath}« that defaults to being false, but when explicitly set to »true«, disables all »config« values set by the module.
|
||||
# Every module should, but not all modules do, provide such an option themselves.
|
||||
@ -101,7 +118,7 @@ in rec {
|
||||
moduleArgs = { utils = import "${specialArgs.inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs;
|
||||
module = import fullPath moduleArgs;
|
||||
in { _file = fullPath; imports = [
|
||||
(mergeAttrsRecursive (([ { imports = module.imports or [ ]; options = module.options or { }; config = module.config or { }; } ]) ++ (lib.toList (override module))))
|
||||
(mergeAttrsRecursive ([ { imports = module.imports or [ ]; options = module.options or { }; config = module.config or { }; } ] ++ (lib.toList (override module))))
|
||||
{ disabledModules = [ modulePath ]; }
|
||||
]; };
|
||||
}
|
||||
|
12
lib/misc.nix
12
lib/misc.nix
@ -20,19 +20,7 @@ in rec {
|
||||
<<<${lib.escapeShellArg text} cat >$out/${lib.escapeShellArg "/etc/systemd/system/${unit}.d/override.conf"}
|
||||
'');
|
||||
|
||||
# Given »config.ids« (or equivalent) and a user name, returns the users numeric »uid:gid« pair as string.
|
||||
getOwnership = { gids, uids, ... }: user: "${toString uids.${user}}:${toString gids.${user}}";
|
||||
|
||||
# Given »from« and »to« as »config.my.network.spec.hosts.*«,
|
||||
# picks the first of »to«'s IPs whose required subnet is either empty/any, or a prefix to any of the subnets in »from«:
|
||||
# ip = preferredRoute self.subNets other.routes;
|
||||
# ip6 = preferredRoute self.subNets (builtins.filter (r: r.is6) other.routes);
|
||||
# to.find(({ ip, prefix }) => from.any(_=>_.startsWith(prefix))).ip
|
||||
preferredRoute = from: to: (lib.findFirst ({ prefix, ip, ... }: prefix == "" || (builtins.any (fromSub: startsWith prefix fromSub) from)) { ip = ""; } to).ip;
|
||||
|
||||
# Given a message and any value, traces both the message and the value, and returns the value.
|
||||
trace = message: value: (builtins.trace (message +": "+ (lib.generators.toPretty { } value)) value);
|
||||
|
||||
rpoolOf = hostName: "rpool-${builtins.substring 0 8 (builtins.hashString "sha256" hostName)}";
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
This is a library of bash functions, mostly for NixOS system installation.
|
||||
|
||||
The (paths to these) scripts are meant to me passed in the `scripts` argument to [`mkSystemsFlake`](../flakes.nix#mkSystemsFlake) (see [`flake.nix`](../../flake.nix) for an example), which makes their functions available in the per-host `devShells`/`apps`.
|
||||
The (paths to these) scripts are meant to be passed in the `scripts` argument to [`mkSystemsFlake`](../flakes.nix#mkSystemsFlake) (see [`flake.nix`](../../flake.nix) for an example), which makes their functions available in the per-host [`devShells`/`apps`](../flakes.nix#mkSystemsFlake).
|
||||
Host-specific nix variables are available to the bash functions as `@{...}` through [`substituteImplicit`](../scripts.nix#substituteImplicit) with the respective host as root context.
|
||||
Any script passed later in `scripts` can overwrite the functions of these (earlier) default scripts.
|
||||
|
||||
@ -21,16 +21,16 @@ function install-system {( set -eu # 1: diskPaths
|
||||
# `install-system` Documentation
|
||||
|
||||
The above function performs the mostly automated installation of any `$HOST` from [`../../hosts/`](../../hosts/) to the local disk(s) (or image file(s)) `$DISK`.
|
||||
On a NixOS host, this script can be run by root as: `#` `( cd /etc/nixos/ && nix run .#"$HOST" -- install-system "$DISK" )`.
|
||||
On a NixOS host, this can be run by root as: `#` `nix run .#"$HOST" -- install-system "$DISK"`.
|
||||
|
||||
Doing an installation on non-NixOS (but Linux), where nix isn't installed for root, is a bit of a hack, but works as well.
|
||||
In this case, all `nix` commands will be run as `$SUDO_USER`, but this script and some other user-owned (or user-generated) code will (need to) be run as root.
|
||||
If that is acceptable, run with `sudo` as first argument: `$` `( cd /etc/nixos/ && nix run .#"$HOST" -- sudo install-system "$DISK" )` (And then maybe `sudo bash -c 'chown $SUDO_USER: '"$DISK"` afterwards.)
|
||||
If that is acceptable, run with `sudo` as first argument: `$` `nix run .#"$HOST" -- sudo install-system "$DISK"` (And then maybe `sudo bash -c 'chown $SUDO_USER: '"$DISK"` afterwards.)
|
||||
|
||||
The `nix run` in the above commands substitutes a number of `@{`-prefixed variables based on the `$HOST` name and its configuration from [`../hosts/`](../hosts/), and then sources this script and calls the `install-system` function.
|
||||
If `$DISK` points to something in `/dev/`, then it is directly formatted and written to as block device, otherwise `$DISK` is (re-)created as raw image and then used as loop device.
|
||||
For hosts that install to multiple disks, pass a `:`-separated list of `<disk-name>=<path>` pairs (the name may be omitted only for the `default` disk).
|
||||
For hosts that install to multiple disks, pass a `:`-separated list of `<disk-name>=<path>` pairs (the name may be omitted only for the "`default`" disk).
|
||||
|
||||
Once done, the disk can be transferred -- or the image be copied -- to the final system, and should boot there.
|
||||
If the host's hardware target allows, a resulting image can also be passed to [`register-vbox`](../lib/setup-scripts/maintenance.sh#register-vbox) to create a bootable VirtualBox instance for the current user.
|
||||
If the host's hardware target allows, a resulting image can also be passed to [`register-vbox`](../maintenance.sh#register-vbox) to create a bootable VirtualBox instance for the current user, or to [`run-qemu`](../maintenance.sh#run-qemu) to start it in a qemu VM.
|
||||
|
||||
The "Installation" section of each host's documentation should contain host specific details, if any.
|
||||
|
@ -73,7 +73,7 @@ function partition-disks { { # 1: diskPaths
|
||||
if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev's serial ($actual) does not match the serial (${disk[serial]}) declared for ${disk[name]}" ; exit 1 ; fi
|
||||
fi
|
||||
# can (and probably should) restore the backup:
|
||||
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup "${blockDevs[${disk[name]}]}" >$beQuiet )
|
||||
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup "${blockDevs[${disk[name]}]}" >$beQuiet )
|
||||
#partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}"
|
||||
done
|
||||
@{native.parted}/bin/partprobe "${blockDevs[@]}"
|
||||
@ -92,7 +92,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
declare -a sgdisk=( --zap-all ) # delete existing part tables
|
||||
if [[ ${disk[gptOffset]} != 0 ]] ; then
|
||||
sgdisk+=( --move-main-table=$(( 2 + ${disk[gptOffset]} )) ) # this is incorrectly documented as --adjust-main-table in the man pages (at least versions 1.05 to 1.09 incl)
|
||||
sgdisk+=( --move-secondary-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) )
|
||||
sgdisk+=( --move-backup-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) )
|
||||
fi
|
||||
sgdisk+=( --disk-guid="${disk[guid]}" )
|
||||
|
||||
@ -124,13 +124,13 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
|
||||
# move the selected »mbrParts« to slots 1[2[3]] instead of 2[3[4]] (by re-creating part1 in the last sector, then sorting)
|
||||
n;p;1 # new ; primary ; part1
|
||||
$(( ($devSize/512) - 1)) # start (size 1sec)
|
||||
$(( ($devSize/512) - 1)) # start (size 1sec)
|
||||
x;f;r # expert mode ; fix order ; return
|
||||
d;$(( (${#disk[mbrParts]} + 1) / 2 + 1 )) # delete ; part(last)
|
||||
|
||||
# create GPT part (spanning primary GPT area) as last part
|
||||
# create GPT part (spanning primary GPT area and its padding) as last part
|
||||
n;p;4 # new ; primary ; part4
|
||||
1;33 # start ; end
|
||||
1;$(( 33 + ${disk[gptOffset]} )) # start ; end
|
||||
t;4;ee # type ; part4 ; GPT
|
||||
|
||||
${disk[extraFDiskCommands]}
|
||||
|
@ -69,6 +69,7 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
|
||||
mkdir -p -m 755 $mnt/nix/var/nix ; mkdir -p -m 1775 $mnt/nix/store
|
||||
if [[ ${SUDO_USER:-} ]] ; then chown -R $SUDO_USER: $mnt/nix/store $mnt/nix/var ; fi
|
||||
( set -x ; time nix copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; rm -rf $mnt/nix/var/nix/gcroots
|
||||
# TODO: if the target has @{config.nix.autoOptimiseStore} and the host doesn't (there is no .links dir?), optimize now
|
||||
if [[ ${SUDO_USER:-} ]] ; then chown -R root:root $mnt/nix $mnt/nix/var ; chown :30000 $mnt/nix/store ; fi
|
||||
|
||||
# Link/create files that some tooling expects:
|
||||
@ -83,9 +84,9 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
|
||||
mkdir -p -m 755 $mnt/nix/var/nix/profiles ; ln -sT $(realpath $targetSystem) $mnt/nix/var/nix/profiles/system-1-link ; ln -sT system-1-link $mnt/nix/var/nix/profiles/system
|
||||
|
||||
# Support cross architecture installation (not sure if this is actually required)
|
||||
if [[ $(cat /run/current-system/system 2>/dev/null || echo "x86_64-linux") != "@{config.preface.hardware}"-linux ]] ; then
|
||||
mkdir -p $mnt/run/binfmt ; cp -a {,$mnt}/run/binfmt/"@{config.preface.hardware}"-linux || true
|
||||
# Ubuntu (by default) expects the "interpreter" at »/usr/bin/qemu-@{config.preface.hardware}-static«.
|
||||
if [[ $(cat /run/current-system/system 2>/dev/null || echo "x86_64-linux") != "@{config.wip.preface.hardware}"-linux ]] ; then
|
||||
mkdir -p $mnt/run/binfmt ; cp -a {,$mnt}/run/binfmt/"@{config.wip.preface.hardware}"-linux || true
|
||||
# Ubuntu (by default) expects the "interpreter" at »/usr/bin/qemu-@{config.wip.preface.hardware}-static«.
|
||||
fi
|
||||
|
||||
# Run the main install command (primarily for the bootloader):
|
||||
|
@ -51,12 +51,12 @@ function run-qemu {( set -eu # 1: diskImages
|
||||
diskImages=${argv[0]}
|
||||
if [[ ${args[debug]:-} ]] ; then set -x ; fi
|
||||
|
||||
qemu=( @{native.qemu_full}/bin/qemu-system-@{config.preface.hardware} )
|
||||
qemu=( @{native.qemu_full}/bin/qemu-system-@{config.wip.preface.hardware} )
|
||||
qemu+=( -m ${args[mem]:-2048} -smp ${args[smp]:-4} )
|
||||
|
||||
if [[ @{config.preface.hardware}-linux == "@{native.system}" && ! ${args[no-kvm]:-} ]] ; then
|
||||
if [[ @{config.wip.preface.hardware}-linux == "@{native.system}" && ! ${args[no-kvm]:-} ]] ; then
|
||||
qemu+=( -cpu host -enable-kvm ) # For KVM to work vBox may not be running anything at the same time (and vBox hangs on start if qemu runs). Pass »--no-kvm« and accept ~10x slowdown, or stop vBox.
|
||||
elif [[ @{config.preface.hardware} == aarch64 ]] ; then # assume it's a raspberry PI (or compatible)
|
||||
elif [[ @{config.wip.preface.hardware} == aarch64 ]] ; then # assume it's a raspberry PI (or compatible)
|
||||
# TODO: this does not work yet:
|
||||
qemu+=( -machine type=raspi3b -m 1024 ) ; args[no-nat]=1
|
||||
# ... and neither does this:
|
||||
@ -74,7 +74,7 @@ function run-qemu {( set -eu # 1: diskImages
|
||||
if [[ ! -e /tmp/qemu-@{config.networking.hostName}-VARS.fd ]] ; then cat @{pkgs.OVMF.fd}/FV/OVMF_VARS.fd > /tmp/qemu-@{config.networking.hostName}-VARS.fd ; fi
|
||||
# https://lists.gnu.org/archive/html/qemu-discuss/2018-04/msg00045.html
|
||||
fi
|
||||
if [[ @{config.preface.hardware} == aarch64 ]] ; then
|
||||
if [[ @{config.wip.preface.hardware} == aarch64 ]] ; then
|
||||
qemu+=( -kernel @{config.system.build.kernel}/Image -initrd @{config.system.build.initialRamdisk}/initrd -append "$(echo -n "@{config.boot.kernelParams[@]}")" )
|
||||
fi
|
||||
|
||||
|
@ -105,7 +105,7 @@ in rec {
|
||||
parseSizeSuffix = decl: let
|
||||
match = builtins.match ''^([0-9]+)(K|M|G|T|P)?(i)?(B)?$'' decl;
|
||||
num = lib.toInt (builtins.head match); unit = builtins.elemAt match 1;
|
||||
exponent = if unit == null then 0 else { K = 1; M = 2; G = 3; t = 4; P = 5; }.${unit};
|
||||
exponent = if unit == null then 0 else { K = 1; M = 2; G = 3; T = 4; P = 5; }.${unit};
|
||||
base = if (builtins.elemAt match 3) == null || (builtins.elemAt match 2) != null then 1024 else 1000;
|
||||
in if builtins.isInt decl then decl else if match != null then num * (pow base exponent) else throw "${decl} is not a number followed by a size suffix";
|
||||
|
||||
|
@ -9,16 +9,21 @@ Things that really should be (more like) this by default.
|
||||
|
||||
```nix
|
||||
#*/# end of MarkDown, beginning of NixOS module:
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let
|
||||
prefix = inputs.config.prefix;
|
||||
cfg = config.${prefix}.base;
|
||||
in {
|
||||
|
||||
options.${prefix} = { base = {
|
||||
enable = lib.mkEnableOption "saner defaults";
|
||||
includeNixpkgs = lib.mkOption { description = "»nixpkgs« to include in the system build."; type = lib.types.nullOr lib.types.package; default = null; };
|
||||
includeInputs = lib.mkOption { description = "Whether to include all build inputs to the configuration in the final system, such that they are available for self-rebuilds, in the flake registry, and on the »NIX_PATH« entry (e.g. as »pkgs« on the CLI)."; type = lib.types.bool; default = specialArgs?inputs && specialArgs.inputs?self && specialArgs.inputs?nixpkgs; };
|
||||
}; };
|
||||
|
||||
# Bugfix:
|
||||
imports = [ (lib.wip.overrideNixpkgsModule ({ inherit inputs; } // specialArgs) "misc/extra-arguments.nix" (old: { config._module.args.utils = old._module.args.utils // {
|
||||
escapeSystemdPath = s: builtins.replaceStrings [ "/" "-" " " "." ] [ "-" "\\x2d" "\\x20" "\\x2e" ] (lib.removePrefix "/" s); # The original function does not escape ».«, resulting in mismatching names with units generated from paths with ».« in them.
|
||||
}; })) ];
|
||||
|
||||
config = let
|
||||
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
|
||||
implied = true; # some mount points are implied (and forced) to be »neededForBoot« in »specialArgs.utils.pathsNeededForBoot« (this marks those here)
|
||||
@ -34,23 +39,41 @@ in {
|
||||
}) ({
|
||||
# Robustness/debugging:
|
||||
|
||||
boot.kernelParams = [ "panic=10" "boot.panic_on_fail" ]; # Reboot on kernel panic, panic if boot fails.
|
||||
boot.kernelParams = [ "panic=10" "boot.panic_on_fail" ]; # Reboot on kernel panic (showing the printed messages for 10s), panic if boot fails.
|
||||
# might additionally want to do this: https://stackoverflow.com/questions/62083796/automatic-reboot-on-systemd-emergency-mode
|
||||
systemd.extraConfig = "StatusUnitFormat=name"; # Show unit names instead of descriptions during boot.
|
||||
|
||||
|
||||
}) (lib.mkIf (cfg.includeNixpkgs != null) {
|
||||
}) (lib.mkIf cfg.includeInputs { # non-flake
|
||||
|
||||
nix.registry.nixpkgs.flake = cfg.includeNixpkgs;
|
||||
environment.etc."nix/channels/nixpkgs".source = cfg.includeNixpkgs.outPath;
|
||||
nix.nixPath = [ "nixpkgs=/etc/nix/channels/nixpkgs" "nixos-config=/etc/nixos" ];
|
||||
# Importing »<nixpkgs>« as non-flake returns a lambda returning the evaluated Nix Package Collection (»pkgs«). The most accurate representation of what that should be on the target host is the »pkgs« constructed when building it:
|
||||
system.extraSystemBuilderCmds = ''
|
||||
ln -sT ${pkgs.writeText "pkgs.nix" ''
|
||||
# Provide the exact same version of (nix)pkgs on the CLI as in the NixOS-configuration (but note that this ignores the args passed to it; and it'll be a bit slower, as it partially evaluates the host's configuration):
|
||||
args: (builtins.getFlake ${builtins.toJSON specialArgs.inputs.self.outPath}).nixosConfigurations.${name}.pkgs
|
||||
''} $out/pkgs # (nixpkgs with overlays)
|
||||
''; # (use this indirection so that all open shells update automatically)
|
||||
|
||||
nix.nixPath = [ "nixpkgs=/run/current-system/pkgs" ]; # this intentionally replaces the defaults: nixpkgs is here, /etc/nixos/flake.nix is implicit, channels are impure
|
||||
# TODO: decide whether to put any other flake inputs also on the nix path: con: they may very well not even have a »./default.nix«
|
||||
nix.autoOptimiseStore = true; # because why not ...
|
||||
environment.shellAliases = { "with" = ''nix-shell --run "bash --login" -p''; }; # »with« doesn't seem to be a common linux command yet, and it makes sense here: with $package => do stuff in shell
|
||||
|
||||
}) (lib.mkIf cfg.includeInputs { # flake things
|
||||
|
||||
# "input" to the system build is definitely also a nix version that works with flakes:
|
||||
nix.extraOptions = "experimental-features = nix-command flakes"; # apparently, even nix 2.8 (in nixos-22.05) needs this
|
||||
environment.shellAliases = { "with" = ''nix-shell --run "bash --login" -p''; };
|
||||
system.extraSystemBuilderCmds = (if !specialArgs?inputs && !specialArgs.inputs?self then "" else ''
|
||||
ln -sT ${specialArgs.inputs.self.outPath} $out/config # (build input for reference)
|
||||
'');
|
||||
environment.systemPackages = [ pkgs.git ]; # necessary as external dependency when working with flakes
|
||||
|
||||
# »inputs.self« does not have a name (that is known here), so just register it as »/etc/nixos/« system config:
|
||||
environment.etc.nixos.source = lib.mkDefault "/run/current-system/config"; # (use this indirection to prevent every change in the config to necessarily also change »/etc«)
|
||||
system.extraSystemBuilderCmds = ''
|
||||
ln -sT ${specialArgs.inputs.self.outPath} $out/config # (build input for reference)
|
||||
'';
|
||||
|
||||
# Add all inputs to the flake registry:
|
||||
nix.registry = lib.mapAttrs (name: input: lib.mkDefault { flake = input; }) (builtins.removeAttrs specialArgs.inputs [ "self" ]);
|
||||
|
||||
|
||||
}) ({
|
||||
# Free convenience:
|
||||
@ -70,10 +93,10 @@ in {
|
||||
fi
|
||||
fi
|
||||
export TERM_RECURSION_DEPTH=$(( 1 + ''${TERM_RECURSION_DEPTH:-0} ))
|
||||
''; # The non-interactive version of bash does not remove »\[« and »\]« from PS1, but without those the terminal gets confused about the cursor position after the prompt once one types more than a bit of text there, at least via serial or SSH.
|
||||
''; # The non-interactive version of bash does not remove »\[« and »\]« from PS1, but without those the terminal gets confused about the cursor position after the prompt once one types more than a bit of text there (at least via serial or SSH).
|
||||
|
||||
environment.interactiveShellInit = lib.mkDefault ''
|
||||
if [[ "$(realpath /dev/stdin)" == /dev/ttyS* && $LINES == 24 && $COLUMNS == 80 ]] ; then
|
||||
if [[ "$(realpath /dev/stdin)" != /dev/tty[1-8] && $LINES == 24 && $COLUMNS == 80 ]] ; then
|
||||
stty rows 34 cols 145 # Fairly large font on 1080p. Definitely a better default than 24x80.
|
||||
fi
|
||||
'';
|
||||
|
@ -9,9 +9,7 @@ Filesystem related "patches" of options in nixpkgs, i.e. additions of options th
|
||||
|
||||
```nix
|
||||
#*/# end of MarkDown, beginning of NixOS module:
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let
|
||||
utils = import "${inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; };
|
||||
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, utils, ... }: let inherit (inputs.self) lib; in let
|
||||
in {
|
||||
|
||||
options = {
|
||||
|
@ -95,11 +95,11 @@ On a less beefy system, but also with less data to manage, `tmpfs` works fine fo
|
||||
|
||||
```nix
|
||||
#*/# end of MarkDown, beginning of NixOS module:
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let
|
||||
dirname: inputs: specialArgs@{ config, pkgs, lib, utils, ... }: let inherit (inputs.self) lib; in let
|
||||
prefix = inputs.config.prefix;
|
||||
cfg = config.${prefix}.fs.temproot;
|
||||
utils = import "${inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; };
|
||||
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
|
||||
keep = if cfg.remote.type == "none" then "local" else "remote"; # preferred place for data that should be kept
|
||||
|
||||
optionsFor = type: desc: {
|
||||
bind.source = lib.mkOption { description = "Prefix for bind-mount targets."; type = lib.types.str; default = "/.${type}"; };
|
||||
@ -151,7 +151,8 @@ in {
|
||||
type = lib.mkOption { description = ''
|
||||
"bind": Expects a filesystem to be mounted at ».bind.target« that gets backed. Bind-mounts any additional mounts to ».bind.source+"/"+<mount.source>« (creating those paths if necessary).
|
||||
"zfs": ...
|
||||
''; type = lib.types.enum [ "bind" "zfs" ]; default = "bind"; };
|
||||
"none": Don't provide a »/remote«. For hosts that have no secrets or important state.
|
||||
''; type = lib.types.enum [ "bind" "zfs" "none" ]; default = "bind"; };
|
||||
} // (optionsFor "remote" "remotely backed-up");
|
||||
|
||||
swap = {
|
||||
@ -172,9 +173,9 @@ in {
|
||||
"/tmp" = { mode = "1777"; };
|
||||
};
|
||||
fs.temproot.local.mounts = {
|
||||
"/local" = { source = "system"; mode = "755"; };
|
||||
"/nix" = { zfsProps = zfsNoSyncProps; mode = "755"; }; # this (or /nix/store) is required
|
||||
"/var/log" = { source = "logs"; mode = "755"; };
|
||||
"/local" = { source = "system"; mode = "755"; };
|
||||
# »/swap« is used by »cfg.swap.asPartition = false«
|
||||
};
|
||||
fs.temproot.remote.mounts = {
|
||||
@ -187,17 +188,17 @@ in {
|
||||
|
||||
}) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot.
|
||||
|
||||
environment.etc.nixos.source = "/local/etc/nixos";
|
||||
|
||||
systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ...
|
||||
# nixos config
|
||||
"L+ /etc/nixos/ - - - - ../../remote/etc/nixos/"
|
||||
# »/root/.nix-channels« is already being restored.
|
||||
# »/var/lib/nixos« remains ephemeral. Where it matters, explicitly define a U/GID!
|
||||
|
||||
# root's command history
|
||||
"f /remote/root/.bash_history 0600 root root -"
|
||||
"L+ /root/.bash_history - - - - ../remote/root/.bash_history"
|
||||
"f /remote/root/.local/share/nix/repl-history 0600 root root -"
|
||||
"L+ /root/.local/share/nix/repl-history - - - - ../../../../remote/root/.local/share/nix/repl-history"
|
||||
"f /${keep}/root/.bash_history 0600 root root -"
|
||||
"L+ /root/.bash_history - - - - ../${keep}/root/.bash_history"
|
||||
"f /${keep}/root/.local/share/nix/repl-history 0600 root root -"
|
||||
"L+ /root/.local/share/nix/repl-history - - - - ../../../../${keep}/root/.local/share/nix/repl-history"
|
||||
];
|
||||
fileSystems = { # this does get applied early
|
||||
# (on systems without hardware clock, this allows systemd to provide an at least monolithic time after restarts)
|
||||
@ -252,7 +253,7 @@ in {
|
||||
${prefix} = {
|
||||
fs.keystore.keys."luks/local-${hash}/0" = lib.mkIf (cfg.local.bind.base == "f2fs-encrypted") (lib.mkOptionDefault "random");
|
||||
fs.disks.partitions."local-${hash}" = {
|
||||
type = "8300"; order = 1000; disk = "primary"; size = "50%";
|
||||
type = "8300"; order = lib.mkDefault 1000; disk = lib.mkDefault "primary"; size = lib.mkDefault (if cfg.remote.type == "none" then null else "50%");
|
||||
};
|
||||
};
|
||||
fileSystems.${cfg.local.bind.source} = { fsType = "f2fs"; device = "/dev/${if useLuks then "mapper" else "disk/by-partlabel"}/local-${hash}"; formatOptions = lib.mkDefault (lib.concatStrings [
|
||||
|
@ -42,7 +42,7 @@ in {
|
||||
systemd.services."dropbear" = {
|
||||
description = "dropbear SSH server (listening)";
|
||||
wantedBy = [ "multi-user.target" ]; after = [ "network.target" ];
|
||||
serviceConfig.PreExec = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/";
|
||||
serviceConfig.ExecStartPre = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/";
|
||||
serviceConfig.ExecStart = defaultArgs + " -F -E"; # don't fork, use stderr
|
||||
#serviceConfig.PIDFile = "/var/run/dropbear.pid"; serviceConfig.Type = "forking"; after = [ "network.target" ]; # alternative to »-E -F« (?)
|
||||
};
|
||||
|
@ -24,4 +24,7 @@ in {
|
||||
../patches/gptfdisk-move-secondary-table.patch
|
||||
];
|
||||
});
|
||||
|
||||
libblockdev = prev.libblockdev.override { inherit (prev) gptfdisk; };
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ in {
|
||||
hash = "sha256-6cHkr3s7/2BVXBTn9bUfPFbYAfv9VYh6C9GAbWILNjs=";
|
||||
};
|
||||
nativeBuildInputs = [ pkgs.cmake pkgs.zlib ];
|
||||
outputs = [ "out" "lib" ];
|
||||
|
||||
meta = {
|
||||
homepage = "https://github.com/sbabic/libubootenv";
|
||||
|
55
overlays/uboot-with-mmc-env.nix.md
Normal file
55
overlays/uboot-with-mmc-env.nix.md
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
# U-Boot with Env Compiled and on MMC
|
||||
|
||||
A function reconfiguring an u-boot package to save its env on MMC (e.g. internal boot storage or microSD) if it doesn't already, and to compile a custom default env into u-boot itself.
|
||||
|
||||
|
||||
## Implementation
|
||||
|
||||
```nix
|
||||
#*/# end of MarkDown, beginning of NixPkgs overlay:
|
||||
dirname: inputs: final: prev: let
|
||||
inherit (final) pkgs; inherit (inputs.self) lib;
|
||||
in {
|
||||
|
||||
uboot-with-mmc-env = {
|
||||
base ? null,
|
||||
|
||||
# The (maximum) total size, position (each in in bytes), and MMC device number of the u-boot env that can be set later:
|
||||
envSize ? base.envSize or 16384,
|
||||
envOffset ? base.envOffset or 4194304,
|
||||
envMmcDev ? 1,
|
||||
# Default boot variables for u-boot. This amends the variables that will be compiled into u-boot as the default env, but also is the base for the variables written by the ».mkEnv« function. As such, this should include everything necessary to boot something:
|
||||
defaultEnv ? ({ }), # $src/scripts/get_default_env.sh can read this again
|
||||
# Lines to append to the u-boot config:
|
||||
extraConfig ? [ ],
|
||||
|
||||
}: base.overrideAttrs (old: let
|
||||
envTxt = env: pkgs.writeText "uboot-env.txt" "${lib.concatStrings (lib.mapAttrsToList (k: v: if v == null then "" else "${k}=${toString v}\n") env)}";
|
||||
defaultEnv' = (base.defaultEnv or { }) // defaultEnv;
|
||||
in {
|
||||
passthru = (old.passthru or { }) // {
|
||||
inherit envSize envOffset; defaultEnv = defaultEnv';
|
||||
|
||||
# Creates a (user) env blob for this u-boot by merging »env« over its »defaultEnv«. The resulting file can be flashed to »CONFIG_ENV_OFFSET« to replace the default env.
|
||||
mkEnv = env: pkgs.runCommandLocal "uboot-env.img" {
|
||||
env = envTxt (defaultEnv' // env);
|
||||
} "${pkgs.ubootTools}/bin/mkenvimage -p 0x00 -s ${toString envSize} -o $out $env";
|
||||
|
||||
};
|
||||
extraConfig = (old.extraConfig or "") + "${lib.concatStringsSep "\n" ([
|
||||
# (these need to be passed as 0x<hex>:)
|
||||
"CONFIG_ENV_OFFSET=0x${lib.concatStrings (map toString (lib.toBaseDigits 16 envOffset))}"
|
||||
"CONFIG_ENV_SIZE=0x${lib.concatStrings (map toString (lib.toBaseDigits 16 envSize))}"
|
||||
# Ensure that env is configured to be stored on MMC(/microSD):
|
||||
"CONFIG_ENV_IS_IN_MMC=y" "CONFIG_SYS_MMC_ENV_DEV=${toString envMmcDev}" # (not sure this is enough)
|
||||
# CONFIG_EXTRA_ENV_SETTINGS here would be overwritten, and CONFIG_DEFAULT_ENV_FILE replaces some basics that should be kept.
|
||||
] ++ extraConfig)}\n";
|
||||
CONFIG_EXTRA_ENV_SETTINGS = ''${lib.concatMapStringsSep ''"\0"'' builtins.toJSON (lib.mapAttrsToList (k: v: if v == null then "" else ''${k}=${toString v}'') defaultEnv')}"\0"''; # (this is in addition to whatever u-boot derives from its other CONFIG_*)
|
||||
postConfigure = (old.postConfigure or "") + ''
|
||||
# Set CONFIG_EXTRA_ENV_SETTINGS just before it's used, to make sure it actually applies:
|
||||
printf "%s\n%s\n" "#define CONFIG_EXTRA_ENV_SETTINGS $CONFIG_EXTRA_ENV_SETTINGS" "$(cat include/env_default.h)" >include/env_default.h
|
||||
'';
|
||||
});
|
||||
}
|
@ -3,11 +3,6 @@ dirname: inputs: let
|
||||
getNamedPatchFiles = dir: builtins.removeAttrs (builtins.listToAttrs (map (name: let
|
||||
match = builtins.match ''^(.*)[.]patch$'' name;
|
||||
in if (match != null) then {
|
||||
name = builtins.head match; value = "${dir}/${name}";
|
||||
name = builtins.head match; value = builtins.path { path = "${dir}/${name}"; inherit name; }; # »builtins.path« puts the file in a separate, content-addressed store path, ensuring it's path only changes when the content changes, thus avoiding unnecessary rebuilds.
|
||||
} else { name = ""; value = null; }) (builtins.attrNames (builtins.readDir dir)))) [ "" ];
|
||||
in (getNamedPatchFiles dirname) // {
|
||||
# When referring to the patches by a path derived from »dirname«, then their paths change whenever that changes, which happens when any file in this repo changes. Changing patch paths mean that the derivations the patches are inputs to need to be rebuilt, so using local paths, which put their targets into a new store artifact (i.e. separate input) is much more efficient.
|
||||
# TODO: automate this, somehow:
|
||||
nixpkgs-fix-systemd-boot-install = ./nixpkgs-fix-systemd-boot-install.patch;
|
||||
nixpkgs-test = ./nixpkgs-test.patch;
|
||||
}
|
||||
in (getNamedPatchFiles dirname)
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/gpt.cc b/gpt.cc
|
||||
index 76cd9ad..375c216 100644
|
||||
index 76cd9ad..d61064f 100644
|
||||
--- a/gpt.cc
|
||||
+++ b/gpt.cc
|
||||
@@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) {
|
||||
@ -17,7 +17,7 @@ index 76cd9ad..375c216 100644
|
||||
|
||||
+// Change the start sector for the secondary partition table.
|
||||
+// Returns 1 on success, 0 on failure
|
||||
+int GPTData::MoveSecondaryTable(uint64_t pteSector) {
|
||||
+int GPTData::MoveSecondTable(uint64_t pteSector) {
|
||||
+ uint64_t pteSize = GetTableSizeInSectors();
|
||||
+ int retval = 1;
|
||||
+
|
||||
@ -30,7 +30,7 @@ index 76cd9ad..375c216 100644
|
||||
+ retval = 0;
|
||||
+ } // if/else
|
||||
+ return retval;
|
||||
+} // GPTData::MoveSecondaryTable()
|
||||
+} // GPTData::MoveSecondTable()
|
||||
+
|
||||
// Blank the partition array
|
||||
void GPTData::BlankPartitions(void) {
|
||||
@ -66,14 +66,14 @@ index 76cd9ad..375c216 100644
|
||||
// space on the disk. Returns 0 if there are no available blocks left
|
||||
uint64_t GPTData::FindFirstInLargest(void) {
|
||||
diff --git a/gpt.h b/gpt.h
|
||||
index 5d19372..c272d65 100644
|
||||
index 5d19372..17b3380 100644
|
||||
--- a/gpt.h
|
||||
+++ b/gpt.h
|
||||
@@ -142,6 +142,7 @@ public:
|
||||
// Adjust GPT structures WITHOUT user interaction...
|
||||
int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
|
||||
int MoveMainTable(uint64_t pteSector);
|
||||
+ int MoveSecondaryTable(uint64_t pteSector);
|
||||
+ int MoveSecondTable(uint64_t pteSector);
|
||||
void BlankPartitions(void);
|
||||
int DeletePartition(uint32_t partNum);
|
||||
uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
|
||||
@ -95,7 +95,7 @@ index 5d19372..c272d65 100644
|
||||
uint64_t FindLastAvailable();
|
||||
uint64_t FindLastInFree(uint64_t start, bool align = false);
|
||||
diff --git a/gptcl.cc b/gptcl.cc
|
||||
index 34c9421..f6517e4 100644
|
||||
index 34c9421..232285a 100644
|
||||
--- a/gptcl.cc
|
||||
+++ b/gptcl.cc
|
||||
@@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||
@ -107,13 +107,22 @@ index 34c9421..f6517e4 100644
|
||||
uint64_t temp; // temporary variable; free to use in any case
|
||||
char *device;
|
||||
string cmd, typeGUID, name;
|
||||
@@ -85,7 +85,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||
{"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""},
|
||||
{"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"},
|
||||
{"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""},
|
||||
- {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""},
|
||||
+ {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second/backup header to end of disk", ""},
|
||||
{"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""},
|
||||
{"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""},
|
||||
{"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""},
|
||||
@@ -94,7 +94,8 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
|
||||
{"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"},
|
||||
{"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
|
||||
{"align-end", 'I', POPT_ARG_NONE, NULL, 'I', "align partition end points", ""},
|
||||
- {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"},
|
||||
+ {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "change the start sector of the main partition table", "sector"},
|
||||
+ {"move-secondary-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the secondary partition table", "sector"},
|
||||
+ {"move-backup-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the second/backup partition table", "sector"},
|
||||
{"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
|
||||
{"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
|
||||
{"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"},
|
||||
@ -144,7 +153,7 @@ index 34c9421..f6517e4 100644
|
||||
+ } // if/else
|
||||
+ break;
|
||||
+ case 'k':
|
||||
+ if (MoveSecondaryTable(secondTableLBA)) {
|
||||
+ if (MoveSecondTable(secondTableLBA)) {
|
||||
+ JustLooking(0);
|
||||
+ saveData = 1;
|
||||
+ } else {
|
||||
|
Loading…
Reference in New Issue
Block a user