Nix و NixOS برای زیرساخت بازتولیدپذیر، بخش ۳: Flakes و محیط‌های توسعه‌ی بازتولیدپذیر

تا اینجا دیدیم که package و حتی ابزارهای خودمان، expressionهای Nix هستند. اما در عمل به یک ساختار استاندارد نیاز داریم که ورودی‌ها (مثل نسخه‌ی Nixpkgs) را دقیق پین کند تا خروجی روی هر ماشینی یکی باشد. این دقیقاً کاری است که flakes انجام می‌دهد. در این بخش یک محیط توسعه‌ی کاملاً بازتولیدپذیر می‌سازیم.

flake چیست؟

یک flake یک فایل flake.nix با ساختار مشخص است: بخش inputs وابستگی‌های بیرونی (مثل Nixpkgs) را تعریف می‌کند و بخش outputs چیزهایی که flake تولید می‌کند (محیط‌های توسعه، packageها، و بعداً کل پیکربندی NixOS). مهم‌ترین ویژگی، فایل flake.lock است که نسخه‌ی دقیق هر input را قفل می‌کند؛ درست مثل lockfile در ابزارهای دیگر.

یک dev shell بازتولیدپذیر

در این flake یک devShell تعریف می‌کنیم که دقیقاً سه ابزار (jq، ripgrep و python3) را فراهم می‌کند. با nix develop وارد یک shell می‌شویم که همان نسخه‌های پین‌شده در آن حاضرند، بدون اینکه چیزی روی سیستم میزبان نصب شود:

فایل flake.nix و ورود به dev shell با nix develop و نمایش نسخه‌های پین‌شده
یک flake.nix با یک devShell، و ورود به آن با nix develop. هر سه ابزار به مسیرهای دقیق /nix/store اشاره می‌کنند و نسخه‌هایشان مشخص و ثابت است.

پین‌کردن با flake.lock

وقتی flake را قفل می‌کنیم، نسخه‌ی دقیق Nixpkgs (یک revision مشخص از Git به‌همراه hash محتوایش) در flake.lock ثبت می‌شود. از این پس هر کسی که این flake را اجرا کند، دقیقاً همان ورودی را می‌گیرد؛ چه امروز، چه یک سال بعد:

خروجی nix flake metadata و نسخه‌ی قفل‌شده‌ی Nixpkgs در flake.lock
nix flake metadata نشان می‌دهد input به یک revision مشخص از Nixpkgs قفل شده است. همان revision و narHash در flake.lock ثبت شده‌اند و بازتولید را تضمین می‌کنند.

خروجی‌های flake

یک flake می‌تواند چند خروجی داشته باشد. با nix flake show ساختار آن را می‌بینیم و با nix build هر package را می‌سازیم؛ همان ابزار greet بخش قبل، این‌بار به‌صورت یک خروجی flake:

خروجی nix flake show و ساخت package با nix build
nix flake show خروجی‌های flake را فهرست می‌کند (یک devShell و یک package)، و nix build .#greet آن package را می‌سازد و اجرا می‌کند.

فعال‌سازی خودکار با direnv

برای اینکه هر بار دستی nix develop نزنیم، از direnv به‌همراه nix-direnv استفاده می‌کنیم. کافی است در ریشه‌ی پروژه یک فایل .envrc بسازیم؛ از آن پس هر وقت وارد پوشه شویم، محیط به‌صورت خودکار فعال و هنگام خروج غیرفعال می‌شود:

echo "use flake" > .envrc
direnv allow

نتیجه یک تجربه‌ی روان است: هر پروژه محیط خودش را دارد، پین‌شده و بازتولیدپذیر، بدون آلوده‌کردن سیستم اصلی.

جمع‌بندی و بخش بعد

با flakes حالا می‌توانیم محیط‌ها و packageها را به‌صورت کد و با نسخه‌های قفل‌شده تعریف کنیم؛ چیزی که روی لپ‌تاپ، سرور و CI یکسان کار می‌کند. قدم بعدی طبیعی است: اگر بتوان یک محیط را این‌طور توصیف کرد، چرا کل سیستم‌عامل را نه؟

در بخش بعد وارد NixOS می‌شویم: یک سیستم‌عامل که کل پیکربندی‌اش در یک فایل توصیف می‌شود و با یک دستور ساخته یا به نسخه‌ی قبل برگردانده می‌شود.

بخش‌های این مجموعه

← بازگشت به وبلاگ