Milda Enkonduko al Nix-Flokoj
Tamen ĉiu decido por io estas decido kontraŭ io alia.
—H. G. Tannhaus, Dark (2017)
Table of contents
Enkonduko
Kiam mi malkovris je Nix preskaŭ du jardekoj antaŭe, mi lernis, ke estas ankoraŭ multaj aferoj kiujn mi ne scias. Mi estis forblovita. Mi miris. Ĝi estis mirinda. Mia ardo por sistemadministrado reardiĝis.
Kiel iu kiu elspezis multan tempon en la BSD-lando—agordante ĉion mane, memorante ĉiom da lokoj kiu grava agordo loĝis—mi opiniis, ke Nix kaj NixOS estas spiro de freŝa aero.
Post ne longe, NixOS fariĝis mia ĉefa labora maŝino. Mi povis fari ĉion per ĝi, eĉ uzi aparatojn kiuj ne estis antaŭdesignita por funkcii sur linukso. Per la helpo de la oficiala dokumentoj kaj helpemaj artikoloj de homoj kiuj jam uzis ĝin antaŭ mi, mi povis agordi ĝin laŭ miaj preferoj. Mi skribis pri tio sperto ĉi tie.
NixOS
La maniero en kiu mi ĉiam uzis mian NixOS-sistemon estas, ke mi instalu pakojn per nix-env
. Bedaŭrinde, tutegale kiom mi optimumigis la procedon, ĝi estis ankoraŭ malrapida kaj laborintensiva. Mi malkovris, ke la sistemo ŝargis aĵojn pli ol necesitaj, severe influi la rendimenton.
Mi pli fosis kaj malkovris la Nix-flokojn. Estis menciite ĉe la vikio, ke ĝi estas eksperimenta kapablo. Mi fakte ne scias kion tio signifas, sed ĝi sentis kiel alfaa kapablo kiu estas jam preta.
Por ebligi ĝin, mi redaktis la dosieron /etc/nixos/configuration.nix
kaj aldonis la jenan:
nix.settings.experimental-features = [ "nix-command" "flakes" ];
kaj faris la jenan
sudo nixos-rebuild switch
Mi akiris novan aron de kamondoj el la nix
-komando, kaj malkovris ke ili estas malbagatelaj foriroj el la komandoj kiuj mi jam konis.
Por instali pakon, ekzemple emem—sen flokoj kaj por malinstali ĝin, ruli
nix-env --install -A nixpkgs.emem
nix-env --uninstall emem
Per flokoj—por instali kaj malinstali ĝin—ruli
nix profile install nixpkgs#emem
nix profile remove emem
Darwin
Kiam mi akiris M1 Macbook Pro-tekkomputilon, mi nature sciemis ĉu estas maniero por mi por instali je Nix sur ĝi. Post ne longe, mi malkovris nix-darwin. Post unu horo de alĝustigetado, mi fine akiris la sorĉelvokon kiu muntus ĉion. Same kiel flokoj sur NixOS, mi prenis la novan aron de komandoj.
Plejmulte da—se ne ĉiom—da gravaj Nix-komandoj jam kunfandiĝis al nix
. Mi iris per ĝi senprobleme dum unu jaro. Baldaŭ, mi decidis, ke estas jam la ŝanco por plenplonĝi kaj uzi flokojn ekster la baza agordo.
Flokoj
Unu el la aferoj kiuj ja ĉiam ĝenis mi estis la transiro el la malnova reĝimo de uzi shell.nix
por krei porteblajn nix-ŝelojn, al flake.nix
. Por uzi flokojn, oni devas krei la dosieron flake.nix
, kiu estos la bazo por ĉio. La komando nix
legas ĉi tiun dosieron el la aktuala dosierujo, implicite. La init
sub-komando kreas unu, por ni, oportune.
nix flake init
La rezulto, flake.nix
, aspektos kiel la jena:
{
description = "A flake️️";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
};
outputs = { self, nixpkgs }: {
packages.x86-64_linux.hello = nixpkgs.legacyPackages.x86-64_linux.hello;
packages.x86-64_linux.default = self.packages.x86-64_linux.hello;
};
}
Oni povas vidi, tuje, ke ĝi estas atribua aro, de tri parto:
{
description = ...;
inputs = ...;
outputs = ...;
}
Metu plaĉan valoron en description
por ke ni povu uzi ĝin kiel informon kiam «grep» por flokoj. inputs
indikas la aferojn kiuj iros al la floko, dum outputs
estas tiuj kiuj estos produktita per ĝi, kiam tiam estos uzita de la nix
-komandoj. inputs
mem estas atribua aro, kaj ni unue difinos la lokon de nixpkgs
.
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
}
Jen ni uzas ref
por indiki la branĉan nomon. Oni povas havi aliajn indikilojn, kiel enmeta-identigilo, per rev
.
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable";
oldnixpkgs.url = "github:nixos/nixpkgs?rev=d73ab2f14214a587059fa38cacf82198409e54eb";
}
La valoro de outputs
estas funkcio kiu ricevas atribuan liston kiel argumento kaj revenas atribuan liston enhavante la eligajn specifojn. La formo estas la jena:
outputs = { }: { };
La eligoj de floko kongruas kun specifaj Nix-komandoj. Kelke da ili kiujn mi uzas estas listigita jene.
eligo | uzita de |
---|---|
packages | nix build |
devShells | nix develop |
apps | nix run |
nixosConfigurations | nixos-rebuild –flake |
darwinConfigurations | darwin-rebuild –flake |
packages
Ni priparolu la plej bazan tipon de eligo—packages.
outputs = { self, nixpkgs }: {
packages.x86-64_linux.hello = nixpkgs.legacyPackages.x86-64_linux.hello;
packages.x86-64_linux.default = self.packages.x86-64_linux.hello;
};
La argumento de tiu funkcio estas atribua aro, kun du ŝlosilaj: self
kaj nixpkgs
. self
, tie, estas la atribuo mem. Tio ebligas nin por skribi referencojn al la partoj ene. nixpkgs
enhavas ĉiom da pakoj por specifa sistemo, kiu, en la ekzemplo supre, estas x86-64_linux
.
En
packages.x86-64_linux.hello = nixpkgs.legacyPackages.x86-64_linux.hello;
ni kreas eligan pakon kiu nomiĝas packages.x86-64_linux.hello
, valorizante ĝin la hello
-derivaĵon el Nixpkgs. Ni ankaŭ devas precizigi la arkitekturon, ĉar pakoj estas sistemspecifaj. Sekve, ni kreu implicitan eligan pakon kiu estos taksita se iu ajn pako ne estas precizigita. Ni uzas la identigilon self.packages.x86-64_linux.hello
por elekti packages.x86-64_linux.hello
kiu estas antaŭe difinita en la sama atribua aro.
Ni restrukturu outputs
por igi ĝin pli legeblan:
outputs = { nixpkgs }:
let
system = "x86-64_linux";
pkgs = nixpkgs.legacyPackages.${system};
in with pkgs; {
packages.${system} = rec {
hello = pkgs.hello;
default = hello;
};
};
Estas norma praktiko por havi pkgs
-variablon kiu indikas ĉiam da pakoj. Tiam, mi forigis self
el la atribua aro, por ke mi povu havi pli da libero por uzi rec.
Per flokoj, ĉio devas esti enmetita kun gito. La nix
-komando ne funkcios kromse ili estas parto de la deponejo. Ajna .nix
-dosiero estas referencita de la flokoj, devas esti parto de la deponejo.
git init
git add .
git commit -m 'Initial commit'
Por munti la hello
-pakon, ruli
nix build .#hello
La .
-signo indikas la aktualan dosierujon ĉe la floka fonto, dum #hello
indikas, ke ni muntas la hello
-pakon. Se la aktuala dosierujo estas /Users/foo/tmp
, la jenaj komandoj estas ekvivalentaj.
nix build .#hello
nix build $PWD#hello
nix build /Users/foo/tmp#hello
Por munti la implicitan pakon, ni simple forigas #hello
.
nix build
nix build $PWD
nix build .
nix build /Users/foo/tmp
La komando kreas la simbilligilon result
en la aktuala dosierujo kiu indikas la muntan eligon en la Nix-konservejo. Por ruli hello
,
./result/bin/hello
devshells
Eble la eligo kiun mi plej ofte uzas estas devShells
. Ĝi ebligas min krei «programadajn mediojn» (kio ajn tio signifas) kiu esence enhavas mediojn kiu estas tute izolitaj el mia ĉefa sistemo. Ĝi estas (esence) la flaka versio de tiuj kiuj estas kreitaj per nix-shell
.
Jen simpla ekzemplo
devShells.${system} = rec {
lisp = mkShell { buildInputs = [ sbcl ]; };
default = lisp;
};
Tio difinas lispan ŝelon per pkgs.mkShell
kaj prenas atribuan liston. La plej grava ŝlosilo estas buildInputs
kiu estas listo de la pakoj. Ĉi tie, ĝi estas pkgs.sbcl
. Kiel la packages
-eligo, ni difinas implicitan ŝelon per default
.
Nia flake.nix
-dosiero nun aspektas jene
{
description = "A flake️️";
inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable"; };
outputs = { nixpkgs, ... }:
let
system = "x86-64_linux";
pkgs = nixpkgs.legacyPackages.${system};
in with pkgs; {
packages.${system} = rec {
hello = hello;
default = hello;
};
devShells.${system} = rec {
lisp = mkShell { buildInputs = [ sbcl ]; };
default = lisp;
};
};
}
Por eniri la implicitan ŝelon, kiu estas lisp
, ruli
nix develop
Oni tiam havas aliron al la pakoj kiuj oni ĵus deklaris,
sbcl --version
Apps
app
-eligo, aliflanke, ebligas onin por oportune plenumi iu ajn programon el pako.
Ni iru kaj difinu apan eligon kiu lanĉas sisteman kontrolilon.
apps.${system} = rec {
btop = {
type = "app";
program = "${pkgs.btop}/bin/btop";
};
default = btop;
};
La atribua tipo devas havi la valoron "app"
. La atribuo program
enhavas la pakan vojon al la programo kiun oni volas ruli. Por lanĉi ĝin, rulu
nix run
nixosConfiguration
Unu el la plej bonaj aferoj kiun mi malkovris per flakoj estas la kapablo por provizi la administradon de la NixOS-agordo. Oni ne bezonas ŝanĝi ion ajn al la ekzistanta dosiero, /etc/nixos/configuration.nix
. Oni nur devas diri al flake.nix
kiel regi ĝin.
nixosConfigurations."hostname" = nixos.lib.nixosSystem {
modules = [ ./configuration.nix ];
specialArgs = { inherit pkgs; };
};
La dosiero ./configuration.nix
ĉi-supre estas kopio de la dosiero /etc/nixos/configuration.nix
kiu ankaŭ estos spurita per versikontrolo. Aldonu ĝin al la deponejo
git add configuration.nix
La ĉeno "hostname"
devas esti anstataŭigita per la nomo de la maŝino kiu uzus tiun agordon.
{
inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; };
outputs = { nixpkgs, ... }:
let
system = "x86-64_linux";
pkgs = nixpkgs.legacyPackages.${system};
in with pkgs; {
nixosConfigurations = {
"ebzzry-tpad" = nixpkgs.lib.nixosSystem {
modules = [ ./nixos-configuration.nix ];
specialArgs = { inherit pkgs; };
};
};
};
}
Por remunti la NixOS-agordon por la maŝino ebzzry-tpad
, rulu
sudo nixos-rebuild switch --flake .#ebzzry-tpad
Se nur ekzistas unu agordo, la jena komando sufiĉus,
sudo nixos-rebuild switch --flake .
darwinConfiguration
Per nix-darwin
, oni povas fari la saman kiel ĉi-supre,
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nix-darwin.url = "github:lnl7/nix-darwin";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { nixpkgs, nix-darwin }:
let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
in with pkgs; {
darwinConfigurations = {
"ebzzry-mbp" = nix-darwin.lib.darwinSystem {
modules = [ ./darwin-configuration.nix ];
specialArgs = { inherit pkgs; };
};
};
darwinPackages = self.darwinConfigurations."ebzzry-mbp".pkgs;
};
}
Por remunti la Darwin-agordon, rulu
darwin-rebuild switch --flake .
flake-utils
Tre baldaŭ, oni malkovros ke la agordo estas fuŝaĵo:
{
inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable"; };
outputs = { nixpkgs, ... }:
let
nixosSystem = "x86-64_linux";
nixosPackages = nixpkgs.legacyPackages.${nixosSystem};
darwinSystem = "aarch64-darwin";
darwinPackages = nixpkgs.legacyPackages.${darwinSystem};
in {
packages.${nixosSystem} = with nixosPackages; rec {
hello = hello;
default = hello;
};
devShells.${nixosSystem} = with nixosPackages; rec {
lisp = mkShell { buildInputs = [ sbcl ]; };
default = lisp;
};
apps.${nixosSystem} = with nixosPackages; rec {
btop = {
type = "app";
program = "${pkgs.btop}/bin/btop";
};
default = btop;
};
packages.${darwinSystem} = with darwinPackages; rec {
hello = hello;
default = hello;
};
devShells.${darwinSystem} = with darwinPackages; rec {
lisp = mkShell { buildInputs = [ sbcl ]; };
default = lisp;
};
apps.${darwinSystem} = with darwinPackages; rec {
btop = {
type = "app";
program = "${pkgs.btop}/bin/btop";
};
default = btop;
};
};
}
Ĉiu eligo kiun oni kreas por sistemo, devas esti skribita por la aliaj sistemoj, kiun oni deziras subteni. Tie estas ie kie flake-utils helpas. Ĝi estas aro de utilecaj funkcioj kiu helpas en skribi pli bonajn Nix-esprimojn. Unue ni restrukturu la eligojn por igin ilin pli belajn.
apps.nix:
{ pkgs }: rec {
hello = {
type = "app";
program = "${pkgs.hello}/bin/hello";
};
default = hello;
}
packages.nix:
{ pkgs }: rec {
btop = pkgs.btop;
default = btop;
}
shells.nix:
{ nixpkgs, pkgs, ... }:
with pkgs; rec {
lisp = mkShell { buildInputs = [ sbcl ]; };
default = lisp;
}
flake.nix:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
apps = import ./apps.nix { inherit pkgs; };
packages = import ./packages.nix { inherit pkgs; };
devShells = import ./shells.nix { inherit nixpkgs pkgs; };
});
}
Ne forgesu aldoni la novajn .nix
-dosierojn kiujn oni krenas.
Tio kio ekazas ĉi tie estas ke ni aldonas novan enigon, flake-utils
, kaj sekve, ni pasas ĝin al outputs
, fine ni vokas funkcion.
La funkcio flake-utils.lib.eachDefaultSystem
prenas funkcion kiel sia argumento—(system: ...)
. Tiu funkcio prenas unuopan argumenton, system
, kiu kongruas al la haveblaj sistemoj. Ĝi tiam iteracias tra ĉiu sistemo kaj generi la esprimojn.
Finrimarkoj
Per Nix-flokoj, ĉio fariĝas pli deklara, pli komprenebla, kaj pli lakona. Mi opinias, ke estas facile administri miajn sistemojn per simpla aliro.
Troviĝas ĉiom da dosieroj kiujn mi uzas en ĉi tiu artikolo ĉi tie.
Ĝi povas ŝanĝiĝi en la estonteco, tamen, flokoj nun, estas la plej bona maniero por administri pakojn kaj agordojn. Oni provu uzi ĝin!