فا نیکسی

محیط‌های شل اعلامی با shell.nix

بررسی اجمالی

محیط‌های شل اعلامی به شما امکان می‌دهند که:

  • دستورات Bash را به طور خودکار در طول فعال‌سازی محیط اجرا کنید
  • متغیرهای محیطی را به طور خودکار تنظیم کنید
  • تعاریف محیط را تحت کنترل نسخه قرار داده و آن‌ها را روی ماشین‌های دیگر بازتولید کنید

چه چیزی خواهید آموخت؟

در آموزش ad-hoc-envs، یاد گرفتید که چگونه با استفاده از nix-shell -p به روش دستوری (imperative) محیط‌های شل ایجاد کنید. این کار زمانی که می‌خواهید بدون نصب دائم ابزارها به سرعت به آن‌ها دسترسی داشته باشید عالی است. همچنین یاد گرفتید که چگونه آن دستور را با نسخه‌ای خاص از Nixpkgs با استفاده از یک کامیت گیت به عنوان آرگومان اجرا کنید تا همان محیط قبلی بازآفرینی شود.

در این آموزش، بررسی خواهیم کرد که چگونه با استفاده از یک پیکربندی اعلانی (declarative) در یک فایل Nix، محیط‌های شل بازتولیدپذیر ایجاد کنیم. این فایل را می‌توان با هر کسی به اشتراک گذاشت تا همان محیط را روی ماشینی دیگر بازآفرینی کند.

چقدر زمان می‌برد؟

۳۰ دقیقه

به چه چیزهایی نیاز دارید؟

  • آشنایی با شل یونیکس
  • درک مقدماتی از زبان Nix

ورود به یک شل موقت

فرض کنید محیطی می‌خواهیم که در آن cowsay و lolcat در دسترس باشند. ساده‌ترین راه ممکن برای انجام این کار استفاده از دستور nix-shell -p است:

$ nix-shell -p cowsay lolcat

این دستور کار می‌کند، اما تعدادی معایب دارد:

  • لازم است هر بار که وارد شل می‌شوید، عبارت -p cowsay lolcat را تایپ کنید.
  • این روش به شکلی (ارگونومیک) اجازه نمی‌دهد سفارشی‌سازی‌های بیشتری روی محیط شل خود انجام دهید.

راه‌حل بهتر این است که محیط شل خود را از یک فایل shell.nix بسازیم.

یک فایل shell.nix پایه‌ای

فایلی به نام shell.nix با محتوای زیر ایجاد کنید:

let
  nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
  pkgs = import nixpkgs { config = {}; overlays = []; };
in

pkgs.mkShellNoCC {
  packages = with pkgs; [
    cowsay
    lolcat
  ];
}

جزئیات: توضیحات تفصیلی

ما از نسخه‌ای از Nixpkgs سنجاق‌شده به یک شاخه انتشار استفاده می‌کنیم. اگر آموزش ad-hoc-envs را دنبال کرده‌اید و نمی‌خواهید تمام وابستگی‌ها را دوباره بارگیری کنید، دقیقاً همان ریویژن (revision) مشخص‌شده در بخش towards-reproducibility را تعیین کنید:

> let
>   nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/2a601aafdc5605a5133a2ca506a34a3a73377247";
>   pkgs = import nixpkgs { config = {}; overlays = []; };
> in
> 

```nix
>
> ما به طور صریح `config` و `overlays` را تنظیم می‌کنیم تا از بازنویسی سهوی آن‌ها توسط [global configuration](https://nixos.org/manual/nixpkgs/stable/#chap-packageconfig) جلوگیری کنیم.
>
> تابع `mkShellNoCC` یک مجموعه صفت (attribute set) را به عنوان آرگومان می‌پذیرد.
> در اینجا ما به آن یک صفت `packages` همراه با فهرستی شامل دو آیتم از مجموعه صفت `pkgs` می‌دهیم.
>
> > **جزئیات**: نکته‌ی حاشیه‌ای درباره‌ی `mkShell`
> >
> >
> > دستورات `nix-shell` و `mkShell` در ابتدا به عنوان روشی برای ساخت یک محیط شل حاوی [tools needed to debug package builds](https://nixos.org/manual/nixpkgs/stable/#sec-tools-of-stdenv)، مانند Make یا GCC طراحی شدند.
> > فقط بعدها بود که استفاده از آن به عنوان یک روش عمومی برای ساخت محیط‌های موقت برای اهداف دیگر به طور گسترده‌ای رایج شد.
> > تابع `mkShellNoCC` محیطی از این دست تولید می‌کند، اما بدون زنجیره ابزار کامپایلر (compiler toolchain).
> >
> > ممکن است با نمونه‌هایی از `mkShell` یا `mkShellNoCC` مواجه شوید که در عوض، بسته‌ها را به صفت‌های `buildInputs` یا `nativeBuildInputs` اضافه می‌کنند.
> > تابع `mkShellNoCC` یک [wrapper around `mkDerivation`](https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell) است، بنابراین همان آرگومان‌های `mkDerivation` مانند `buildInputs` یا `nativeBuildInputs` را می‌پذیرد.
> > آرگومان صفت `packages` برای `mkShellNoCC` در واقع صرفاً یک نام مستعار (alias) برای `nativeBuildInputs` است.

با اجرای `nix-shell` در همان پوشه‌ای که فایل `shell.nix` قرار دارد، وارد محیط شوید:

> **نکته**
>
> اولین اجرای `nix-shell` روی این فایل ممکن است برای بارگیری تمام وابستگی‌ها کمی زمان ببرد.

```shell
$ nix-shell
[nix-shell]$ cowsay hello | lolcat

دستور nix-shell به طور پیش‌فرض به دنبال فایلی به نام shell.nix در پوشه جاری می‌گردد و یک محیط شل از روی عبارت نیکس (Nix expression) موجود در این فایل می‌سازد. بسته‌های تعریف‌شده در صفت (attribute) packages در متغیر محیطی $PATH در دسترس خواهند بود.

متغیرهای محیطی

ممکن است بخواهید هنگام ورود به محیط شل، برخی متغیرهای محیطی را به طور خودکار صادر (export) کنید.

متغیر GREETING را تنظیم کنید تا بتوان از آن در محیط شل استفاده کرد:

 let
   nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
   pkgs = import nixpkgs { config = {}; overlays = []; };
 in

 pkgs.mkShellNoCC {
   packages = with pkgs; [
     cowsay
     lolcat
   ];

+  GREETING = "Hello, Nix!";
 }
```nix

هر صفت (attribute) که به `mkShellNoCC` پاس داده شود و جزو موارد رزروشده نباشد و مقدار آن بتواند به یک رشته تبدیل شود، به یک متغیر محیطی تبدیل خواهد شد.

آن را امتحان کنید.
با تایپ کردن `exit` یا فشردن کلیدهای `Ctrl`+`D` از شل خارج شوید، سپس دوباره آن را با `nix-shell` راه‌اندازی کنید.

```shell
[nix-shell]$ echo $GREETING
```nix

> **هشدار**
>
> برخی از متغیرها در برابر تنظیم شدن به شکلی که در بالا توصیف شد، محافظت می‌شوند.
>
> برای مثال، قالب اعلان شل (shell prompt) برای بیشتر شل‌ها توسط متغیر محیطی `PS1` تنظیم می‌شود، اما `nix-shell` از قبل این مورد را به طور پیش‌فرض تنظیم می‌کند و صفت `PS1` تعیین‌شده در آرگومان را نادیده خواهد گرفت.
>
> اگر نیاز دارید این متغیرهای محیطی محافظت‌شده را بازنویسی کنید، طبق توصیف بخش بعدی، از صفت `shellHook` استفاده کنید.

## دستورات راه‌اندازی

ممکن است بخواهید پیش از ورود به محیط شل تعاملی، برخی از دستورات شل را اجرا کنید.
این دستورات را می‌توان در صفت `shellHook` که توسط `mkShellNoCC` فراهم شده است، قرار داد.

برای چاپ یک سلام و احوالپرسی رنگی، `shellHook` را تنظیم کنید:

```diff
 let
   nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
   pkgs = import nixpkgs { config = {}; overlays = []; };
 in

 pkgs.mkShellNoCC {
   packages = with pkgs; [
     cowsay
     lolcat
   ];

   GREETING = "Hello, Nix!";
+
+  shellHook = ''
+    echo $GREETING | cowsay | lolcat
+  '';
 }

دوباره امتحان کنید. با تایپ کردن exit یا فشردن کلیدهای Ctrl+D از شل خارج شوید، سپس آن را دوباره با nix-shell راه‌اندازی کنید تا تأثیر آن را مشاهده کنید.

مراجع

گام‌های بعدی

منبع: nix.dev/first-steps