1 คะแนน โดย GN⁺ 5 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Nix Flakes รวบรวมการพึ่งพาของโปรเจกต์ การล็อก สคีมาของเอาต์พุต และสภาพแวดล้อมสำหรับพัฒนาไว้โดยมี flake.nix และ flake.lock เป็นศูนย์กลาง ส่วน Guix มอบความสามารถประเภทเดียวกันผ่านการผสานเครื่องมือที่แยกเป็นอิสระต่อกัน เช่น channels, manifests, guix describe, guix shell, operating-system
  • Flakes ตรึงการพึ่งพาด้วย inputs ระดับโปรเจกต์และ flake.lock ที่สร้างอัตโนมัติ ขณะที่ Guix สร้างสภาพแวดล้อมที่ทำซ้ำได้ด้วย guix describe ระดับผู้ใช้, channels.scm ที่ระบุ commit ไว้ในโปรเจกต์ และ guix time-machine
  • ความบริสุทธิ์ ถูกบังคับใช้ใน Flakes ผ่าน restricted evaluation ส่วนใน Guix นั้นบรรลุได้ตั้งแต่ระดับการออกแบบผ่านโครงสร้างโมดูล Scheme, อินพุตที่ระบุอย่างชัดเจน และคอนเทนเนอร์ build แบบแยกขาด
  • โครงสร้างเอาต์พุต ของ Flakes มอบ attrset มาตรฐานอย่าง packages, devShells, nixosConfigurations ขณะที่ Guix ใช้เรคคอร์ดและไฟล์ Scheme ที่โปร่งใส เช่น <package>, manifest, operating-system, service ซึ่งแต่ละคำสั่งจะนำไปใช้โดยตรง
  • เกณฑ์ในการเลือก คือถ้าชอบจุดเข้าใช้งานเดียวและสคีมามาตรฐาน Flakes จะเหมาะกว่า แต่ถ้าชอบแนวทางที่ผสานเครื่องมือเล็ก ๆ ซึ่งเป็นอิสระต่อกัน Guix จะเหมาะกว่า

การเปรียบเทียบหลัก

  • ไม่มีฟีเจอร์เดียวใน Guix ที่เทียบเท่า Nix flake โดยตรง ขณะที่ Nix Flakes แก้หลายปัญหาด้วยฟีเจอร์ขนาดใหญ่เพียงตัวเดียว Guix จะตอบโจทย์ด้วยการผสานเครื่องมือที่เล็กกว่าและแยกเป็นอิสระต่อกันมากกว่า
  • Guix นำ Nix daemon กลับมาใช้ใหม่ และใช้คอมโพเนนต์ C++ ร่วมกันสำหรับการแยก build และการจัดการ store
  • Guix เขียนส่วนใหญ่ขึ้นมาใหม่ด้วย Guile Scheme ไม่ว่าจะเป็นภาษา นิยามแพ็กเกจ หรือระบบ service ที่อยู่เหนือ Nix daemon
  • Guix และ Nix ใช้ derivation format อย่าง ATerm และสืบทอดสายของ daemon ร่วมกัน แต่โครงสร้างที่อยู่เหนือ daemon นั้น Guix จัดวางในแบบของตัวเอง
  • Guix มี capabilities ที่ Flakes มอบให้ แต่ให้มาในรูปแบบที่แตกต่างกัน

โครงสร้างพื้นฐานของ Nix Flake

  • Nix flake คือ source tree ที่มีไฟล์ flake.nix อยู่ที่ root และโดยทั่วไปมักอยู่ในรูป Git repository
  • การมีอยู่ของ flake.nix ทำให้ source tree นั้นกลายเป็น flake และไฟล์นี้มีโครงสร้างอย่าง description, inputs, outputs
  • description คือสตริงที่มนุษย์อ่านได้ ซึ่งใช้บอกว่า flake นั้นมอบอะไรให้
  • inputs ใช้ประกาศ dependencies เช่น flakes อื่น ๆ, Git repos, tarballs โดย Nix จะ fetch และ evaluate สิ่งเหล่านี้ก่อนส่งต่อให้ฟังก์ชัน outputs
  • outputs คือฟังก์ชันที่รับอินพุตที่ resolve แล้วและอินพุตพิเศษ self จากนั้นคืนค่า structured attrset ที่บรรจุ packages, dev shells, NixOS configurations, overlays และอื่น ๆ
  • โครงสร้างตัวอย่างและเป้าหมายการทำงาน

    • ตัวอย่าง inputs ที่มี nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; หมายถึงการดึง branch nixos-unstable จาก repository NixOS/nixpkgs บน GitHub
    • flake ตัวอย่างใช้ supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" ]; และ nixpkgs.lib.genAttrs เพื่อสร้าง outputs แยกตาม CPU architecture หลายแบบ
    • Flakes กำหนดให้มีระดับ packages.<system> และในตัวอย่างได้กำหนด package default ใต้ packages ด้วย pkgs.buildGoModule
    • src = ./.; หมายถึงใช้ Git repository ทั้งหมดเป็น source
    • devShells คือคำนิยาม development shell ที่ nix develop จะอ้างอิง โดยในตัวอย่างใช้ pkgs.mkShell และ buildInputs = with pkgs; [ go gopls gotools ];
  • flake.lock และการประเมินแบบบริสุทธิ์

    • เมื่อรันคำสั่ง Nix กับ flake, Nix จะสร้างไฟล์ JSON ชื่อ flake.lock ที่ตรึงทุก input และ transitive input ไว้กับ revision ที่แน่นอน
    • flake.lock คือ lock file ที่ทำให้ build reproducibility เกิดขึ้นได้ข้ามทั้งเครื่องและเวลา
    • Flakes บังคับใช้ pure evaluation โดยไม่ให้ $NIX_PATH, builtins.currentSystem, environment variables หลุดเข้ามาแบบโดยนัย และทุกอย่างต้องระบุอย่าง explicit
    • ฟังก์ชันที่ Flakes ทำมีได้แก่ การประกาศ dependencies, การ pin dependencies, การบังคับใช้ความบริสุทธิ์, การมอบ standard output schema, การแชร์แบบ reproducible และการนิยาม development environments

แนวทางที่ Guix ใช้รับมือ

  • Guix มีแนวทางแก้ปัญหาสำหรับฟีเจอร์จำนวนมากของ Flakes อยู่แล้วตั้งแต่ก่อนที่ Flakes จะถูกนำเข้ามาใน Nix 2.4 เมื่อวันที่ 1 พฤศจิกายน 2021
  • กลไก channels ของ Guix ถูกนำมาใช้ราวปี 2018~2019
  • แนวทางของ Guix มีลักษณะ orthogonal และสามารถใช้เครื่องมือแต่ละตัวแยกจากกันได้โดยไม่เลือกใช้ abstraction แบบ monolithic เพียงชุดเดียว
  • Channels และการประกาศ dependencies

    • ใน Flake จะประกาศ dependencies โดยตรงภายใน flake.nix และในตัวอย่างใช้ nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; กับ home-manager.url = "github:nix-community/home-manager";
    • inputs.nixpkgs.follows = "nixpkgs"; ของ Flake input ทำให้ home-manager ใช้ nixpkgs ของ flake ปัจจุบันแทนที่จะดึง nixpkgs input ของตัวเองมา จึงหลีกเลี่ยงสถานการณ์ที่เกิดสำเนา nixpkgs คนละชุดขึ้นมาสองชุด
    • channels ของ Guix คือ Git repository ที่มี Guile modules อยู่ภายใน โดยปกติจะเก็บ package definitions แต่ก็สามารถรวม services, system configurations และ Scheme code แบบใดก็ได้ด้วย
    • Guix channels จะประกาศใน ~/.config/guix/channels.scm และไฟล์ Scheme นี้จะคืนค่าเป็น list ของ channel records
    • guix pull จะ fetch และ compile channels ทั้งหมด จากนั้นทำให้ modules เหล่านั้นพร้อมใช้งานในทุก guix command
    • Channels สามารถประกาศ dependencies กับ channels อื่นได้ผ่านไฟล์ .guix-channel ที่อยู่ที่ repository root
    • Channel dependency ของ Guix channels มีความคล้ายกับ inputs ของ flake โดยประมาณ และเมื่อรัน guix pull ก็จะมีการ fetch transitive channel dependencies มาด้วย
  • Dependencies รายโปรเจ็กต์และรายผู้ใช้

    • Flakes ใช้แนวทางแบบ per-project โดยแต่ละ repository มี flake.nix และ inputs ของตัวเอง ขณะที่ channels ใช้แนวทาง system-wide หรือ per-user ซึ่ง channels.scm จะมีผลกับทุก guix invocations
    • Flakes รองรับให้โปรเจ็กต์ต่าง ๆ มีชุด dependency ที่ต่างกันได้อย่างเป็นธรรมชาติ ส่วนใน Guix หากต้องการผลลัพธ์แบบเดียวกัน มักจะใช้ guix time-machine หรือ separate profiles
    • Flakes ใช้ไวยากรณ์คล้าย URL เช่น github:NixOS/nixpkgs, git+https://... ขณะที่ channels ใช้ Git URL แบบปกติ
    • ไวยากรณ์ของ Flake ใช้อ้างอิงแบบรวดเร็วได้สะดวกกว่า และ channels นั้นเรียบง่ายกว่าและ explicit กว่า
    • Flakes รองรับ repositories ที่ไม่มี flake.nix เป็น non-flake inputs ด้วย flake = false;
    • ใน Guix นั้น channel คือ Git repository ที่มี Scheme files อยู่ข้างใน จึงไม่ต้องมีการ opt-in แบบพิเศษ และ repository ใดก็ตามที่มี Guile modules ก็สามารถเป็น channel ได้

การตรึงเวอร์ชัน, ความสามารถในการทำซ้ำ, และการย้อนเวลา

  • flake.lock

    • flake.lock เป็น JSON graph โดยทุก input จะถูก pinning ด้วย commit hash ที่แน่นอน และ Nix จะตรวจสอบ narHash ซึ่งเป็น hash ของ source tree ทั้งชุดที่ fetch มา
    • flake.lock จะถูก commit เข้า repository ดังนั้นคนที่ clone ไปจะได้รับ dependency versions ชุดเดียวกัน
    • original ใน flake.lock คือเป้าหมายที่ร้องขอ ส่วน locked คือสิ่งที่ได้มาจริง
    • ระบบ two-layer ของ flake.lock ทำให้สามารถอัปเดตเฉพาะ input บางตัวและคงตัวอื่นไว้ได้ เช่น nix flake lock --update-input nixpkgs
  • guix describe และ guix time-machine

    • Guix จะบันทึก commits ที่แน่นอนของทุก channels เมื่อรัน guix pull และ guix describe จะแสดงข้อมูลนี้
    • เอาต์พุตของ guix describe มีหมายเลข generation, วันที่, สถานะ current, ชื่อ channel, repository URL, branch และ commit
    • Recorded channel commits ของ Guix ทำหน้าที่เทียบได้กับ lock file แต่ไม่ได้อยู่เป็นไฟล์ใน project directory แต่อยู่ในรูป Guile profile ที่ ~/.config/guix/current
    • หากต้องการแชร์ environment ที่ทำซ้ำได้ ใน Guix สามารถใช้ guix time-machine ได้
    • guix time-machine --commit=8a1ab328 -- shell -m manifest.scm จะตรึงตัว Guix เองไว้ที่ revision ที่กำหนดก่อน แล้วจึงรัน guix shell โดยใช้ package definitions ของ revision นั้น
    • guix time-machine จะ download และ compile revision นั้นหากจำเป็น และสร้าง isolated environment ที่ package definitions อยู่ในสถานะของ commit นั้นอย่างแม่นยำ
    • ใน Guix ยังมีแพตเทิร์นที่ check in channels.scm ซึ่งมี pinned commits ไว้ใน project repository ด้วย
    • guix time-machine -C channels.scm -- shell -m manifest.scm จะใช้ channels.scm ที่อยู่ใน repository เพื่อสร้าง environment เดิมกลับมาอย่างแม่นยำ
  • ความแตกต่างของสองแนวทาง

    • flake.lock เป็นแบบ per-project และ automatic ส่วน guix describe เป็นแบบ per-user และ automatic
    • channels.scm ที่มี pinned commits ให้ per-project pinning ใน Guix ได้ แต่เป็นวิธีแบบ manual
    • Guix กำลังปรับปรุง ergonomics ของ per-project pinning แต่ workflow ปัจจุบันยังต้องการการตั้งค่าที่ explicit มากกว่า
    • flake.lock เป็น machine-readable JSON graph ส่วนสิ่งที่เทียบเคียงกันใน Guix คือ Scheme file ที่แสดงรายการ channels พร้อม commit hashes
    • ทั้งสองแนวทางบรรลุเป้าหมายเรื่อง dependency pinning ได้เหมือนกัน แต่ flake lock มีโครงสร้างมากกว่าเพราะเป็น full dependency graph ที่มี original และ locked entries สำหรับทุก transitive input
    • guix time-machine เป็นฟังก์ชันที่ไม่มีสิ่งเทียบตรงตัวใน flake โดยสามารถย้ายไม่ใช่แค่ไปยัง dependency versions ที่ตรึงไว้ แต่ไปยัง historical state ของ package collection ที่แตกต่างออกไปโดยสมบูรณ์ได้

โมเดลความบริสุทธิ์

  • Flakes ทำงานภายใต้บริบทการประเมินผลแบบจำกัด โดยห้ามใช้หรือจะถูกมองข้ามสำหรับ builtins.currentSystem, builtins.getEnv, $NIX_PATH
  • ใน Flakes ทุกอย่างต้องมาจากอินพุตที่ประกาศไว้ ทำให้เกิดการพึ่งพา implicit state โดยไม่ตั้งใจได้ยาก
  • trade-off ของการประเมินผลแบบบริสุทธิ์ใน Flakes คือจำเป็นต้องมีพารามิเตอร์ system แบบชัดเจนกระจายอยู่หลายจุดเพื่อใช้ตรวจจับระบบ และไม่สามารถอ่าน environment variables ได้
  • เมื่อต้องการ impure escape hatch ใน Flakes ต้องส่ง --impure อย่างชัดเจน
  • Guix ไม่ต้องมีโหมดการประเมินผลแบบบริสุทธิ์แยกต่างหาก เพราะตามธรรมเนียมแล้วการประเมินผลนั้นบริสุทธิ์อยู่แล้ว
  • Guile modules จะไม่เข้าถึง environment variables เว้นแต่จะมีการส่งต่อให้อย่างชัดเจน
  • Guix ไม่มีสิ่งที่เทียบเท่ากับ $NIX_PATH และจะ resolve packages ผ่านระบบโมดูลแทนที่จะเป็น search path
  • Guix ไม่มีแนวคิดที่เทียบเท่ากับ builtins.currentSystem โดยระบบจะถูกระบุอย่างชัดเจนผ่าน metadata ของแพ็กเกจและแฟลก --system
  • การ build ของ Guix ก็มีความบริสุทธิ์เช่นกัน โดย builds จะรันอยู่ในคอนเทนเนอร์ที่แยกออกจากกันและมองเห็นเฉพาะอินพุตที่ประกาศไว้อย่างชัดเจนเท่านั้น
  • ใน Guix builds จะไม่มี /usr/bin, /etc, หรือการเข้าถึงเครือข่าย และข้อยกเว้นเรื่องการเข้าถึงเครือข่ายจำกัดอยู่ที่ fixed-output derivations เท่านั้น
  • วิธีการทำ build sandboxing ของ Nix และ Guix ใช้แนวทางแบบเดียวกันโดยพื้นฐาน
  • Guix บรรลุความบริสุทธิ์ในระดับสถาปัตยกรรมผ่านโครงสร้าง Scheme modules ขณะที่ Flakes บังคับใช้ความบริสุทธิ์ด้วยการวาง restricted evaluation mode ทับบนระบบที่เดิมไม่บริสุทธิ์

สคีมาของเอาต์พุตและโมเดลข้อมูล

  • Flake output schema

    • Flakes กำหนด standard schema สำหรับ outputs โดย packages.<system>.<name> ใช้กับ nix build, devShells.<system>.<name> ใช้กับ nix develop, และ apps.<system>.<name> ใช้กับ nix run
    • ใน Flake output schema ยังมี nixosConfigurations.<name>, overlays.<name>, nixosModules.<name>, formatter.<system>, templates.<name>, checks.<system>.<name> ด้วย
    • การทำให้ Flake output schema เป็นมาตรฐานช่วยให้ nix build ., nix run, nix flake show อ้างอิงตำแหน่งที่สอดคล้องกัน จึงเพิ่ม discoverability
    • ข้อเสียของ Flake output schema คือมีความ rigid และหากต้องการเพิ่ม output types ตามอำเภอใจ จำเป็นต้องแก้ไขตัว Nix เอง แม้ว่าจะมีกลไก extension ขนาดเล็กอยู่ก็ตาม
    • เนื่องจากพารามิเตอร์ <system> ของ Flake จึงต้องจัดการ multi-platform support แบบ explicit และมักใช้ helper functions หรือ libraries อย่าง forAllSystems, flake-utils, flake-parts
  • first-class data types ของ Guix

    • Guix ไม่มี output schema เดียวแบบ Flakes แต่มี first-class data types ที่หลายคำสั่งสามารถนำไปใช้ได้
    • ใน Guix packages ถูกนิยามเป็น records แบบ <package> และใช้โดย guix install, guix build
    • ใน Guix manifests ถูกนิยามเป็นไฟล์ Scheme และใช้โดย guix shell -m, guix package
    • ใน Guix system configs ถูกนิยามเป็น operating-system และใช้โดย guix system reconfigure
    • ใน Guix home configs ถูกนิยามเป็น home-environment และใช้โดย guix home reconfigure
    • ใน Guix services ถูกนิยามเป็น records แบบ <service> และใช้ผ่านฟิลด์ services ของ operating-system
    • ใน Guix channels คือ Git repos และใช้โดย guix pull
    • ใน Guix package variants คือ Scheme procedures และใช้โดย --with-input, --transform
  • ไฟล์และนิยามแพ็กเกจ

    • โปรเจกต์ Guix สามารถจัดให้มีได้ทั้ง channel ที่มี package definitions, manifest.scm สำหรับการพัฒนา, system.scm สำหรับการ deploy, รวมถึง declaration ของ operating-system หรือ home-environment ที่นำมาประกอบกัน
    • ใน Guix ไฟล์เหล่านี้ไม่ต้องการ special entry point file และเป็นเพียงไฟล์ Scheme ที่นิยามค่าแบบ Scheme เท่านั้น
    • ใน Guix หากระบุไฟล์ให้กับ guix subcommand ที่เกี่ยวข้อง คำสั่งจะจัดการให้เอง โดยไม่ต้องมี ceremony หรือ schema validation แยกต่างหาก
    • ตัวอย่าง manifest.scm ประกาศ development environment โดยส่งรายการชื่อแพ็กเกจ "guile", "guile-git", "guile-json" ไปยัง specifications->manifest
    • ตัวอย่าง mylib.scm นิยาม record แบบ <package> ซึ่งเป็นสิ่งที่เทียบได้กับ Nix derivation ของ Guix และสามารถ query ฟิลด์ต่าง ๆ ของแพ็กเกจแบบเป็นโปรแกรมได้
    • ตัวอย่าง package definition มี (name "mylib"), (version "0.1.0"), (source (local-file ".")), (build-system gnu-build-system), (inputs (list guile guile-git)), (home-page "https://example.com";), (license gpl3+)
    • local-file ของ Guix จะนำไฟล์จาก current directory มาใช้ในเวลา build และคล้ายกับ src = ./.; ของ Nix
    • gnu-build-system ของ Guix ใช้รูปแบบ ./configure && make && make install และใน Guix ยังมี build systems อื่น ๆ เช่น cmake-build-system, python-build-system
    • Guix กำหนด dependencies ทุกตัวแบบ explicit ต่างจากใน Nix ที่ stdenv จะจัดหา gcc และ coreutils ให้แบบ implicit

สภาพแวดล้อมการพัฒนา

  • ในตัวอย่าง devShells ของ Flakes ใช้ devShells.x86_64-linux.default = pkgs.mkShell { buildInputs = with pkgs; [ go gopls gotools ]; shellHook = '' echo "Welcome to the devShell!" ''; };
  • mkShell จะสร้าง derivation ที่สร้าง shell environment ระหว่างการ build โดย buildInputs จะถูกเพิ่มเข้าไปใน PATH ภายใน shell และ shellHook จะรัน bash ตามต้องการเมื่อเข้าสู่ shell
  • สามารถเข้าสู่ Flake dev shell ได้ด้วย nix develop หรือ nix develop .#my-shell สำหรับ shell ที่ตั้งชื่อไว้
  • สภาพแวดล้อมการพัฒนาใน Guix สามารถกำหนดได้ใน manifest.scm โดยส่ง list ของ package specification strings ให้กับ specifications->manifest
  • ตัวอย่าง Guix manifest ประกาศ "go", "gopls", "go-tools"
  • สามารถเข้าสู่ shell ที่อิงจาก Guix manifest ได้ด้วย guix shell -m manifest.scm
  • ใน ad-hoc environment ของ Guix สามารถส่งแค่ชื่อแพ็กเกจผ่าน command line ได้โดยไม่ต้องมีไฟล์ เช่น guix shell go gopls go-tools
  • guix shell รองรับ --container สำหรับการแยกแบบเต็มรูปแบบ, --emulate-fhs สำหรับรันโปรแกรมที่คาดหวัง standard Linux filesystem layout และ --nesting สำหรับรัน Guix ภายใน Guix container
  • Guix manifests เป็นไฟล์ Scheme แบบ standalone ที่ไม่ได้ฝังอยู่ในโครงสร้าง flake.nix ที่ใหญ่กว่า
  • guix shell ทำงานได้แม้ไม่มีไฟล์ แต่ nix develop ต้องมี flake หรือ shell.nix ของ legacy interface
  • Flakes มี named dev shells เช่น devShells.x86_64-linux.test, devShells.x86_64-linux.default
  • แทนที่จะมี named dev shells, Guix manifests ใช้วิธีวางไฟล์แยกขนานกัน เช่น manifest.scm, test-manifest.scm
  • ทั้ง Nix Flakes และ Guix รองรับการพัฒนาแบบ containerized

การกำหนดค่าระบบ

  • NixOS และ Flakes

    • ในตัวอย่าง nixosConfigurations ของ Flakes, nixpkgs.lib.nixosSystem จะรับ list ของ NixOS modules แล้วสร้าง full system derivation ที่รวม kernel, services, config files เป็นต้น
    • คำสั่งตัวอย่างสำหรับ deploy NixOS แบบ Flake คือ nixos-rebuild switch --flake .#myhost
    • ตัวอย่าง nixosConfigurations.myhost มี system = "x86_64-linux"; และ modules = [ ./configuration.nix home-manager.nixosModules.home-manager ];
    • NixOS modules ใช้ module system ที่มี options, config, mkIf, mkDefault, mkForce พร้อมการ merge ตามลำดับความสำคัญ
    • ระบบโมดูลของ NixOS จะแก้ลำดับความสำคัญให้แม้หลายโมดูลจะตั้งค่า option เดียวกัน ทำให้หลีกเลี่ยงความขัดแย้งได้ง่ายแม้มีโมดูลหลายสิบตัวร่วมกันกำหนดค่าเดียวกัน
  • Guix operating-system

    • operating-system ของ Guix ไม่ใช่ function แต่เป็น Scheme record และแต่ละ field เป็น named typed value ที่ Guix จะทำการ validate
    • คำสั่งตัวอย่างสำหรับ deploy ระบบด้วย Guix คือ guix system reconfigure config.scm
    • ตัวอย่าง operating-system record มี (host-name "myhost"), (timezone "Etc/UTC"), bootloader configuration, file systems และ services
    • ตัวอย่าง bootloader configuration ของ Guix ใช้ grub-efi-bootloader และ target "/boot/efi" โดย Guix รองรับ GRUB, U-Boot เป็นต้น
    • Guix file systems ถูกประกาศเป็น list และ %base-file-systems ให้ค่า default สำหรับ /dev, /proc, /sys เป็นต้น
    • Guix services สร้างเป็น directed acyclic graph(DAG) และแต่ละ service สามารถ extend services อื่นได้
    • %base-services มี services ที่จำเป็น เช่น Shepherd init system, syslog, networking เป็นต้น
    • การกำหนดค่าระบบของ Guix ไม่ต้องมี special output type แค่ระบุไฟล์ที่คืนค่า operating-system record ให้กับ guix system ก็เพียงพอ
    • การประกอบ services ของ Guix ทำให้เขียน service ใหม่ที่เชื่อมเข้ากับระบบเดิมได้ง่ายในแบบที่ยืดหยุ่น

ความสามารถในการสำรวจและรีจิสทรี

  • Flakes มี standard entry point คือ flake.nix ที่ใช้ประกาศ project dependencies, outputs และ schema ที่ค้นพบได้ในไฟล์เดียว
  • โปรเจ็กต์ Guix อาจใช้ไฟล์ตามธรรมเนียม เช่น manifest.scm, channels.scm, guix.scm, package.scm
  • มีความพยายามจะทำให้ guix.scm เป็นไฟล์โปรเจ็กต์มาตรฐานที่ guix shell ตรวจพบได้อัตโนมัติ แต่ยังไม่เป็นที่ยอมรับมั่นคงเท่า flake.nix
  • Flakes มี global registry flake-registry ที่แมปชื่อสั้นกับ URL โดยตัวอย่างคือ nix run nixpkgs#hello, nix build github:NixOS/nixpkgs#firefox
  • เพื่อความสะดวกแบบใกล้เคียงกัน Guix ใช้ package specifications เช่น guix shell hello, guix install firefox
  • Guix ไม่มีสิ่งที่เทียบเท่า registry สำหรับชี้ไปยัง Git repository ใดก็ได้ด้วยชื่อสั้น และจะใช้ URL โดยตรง
  • Nix registry เคยเป็นแหล่งความสับสน เพราะไม่ชัดเจนเสมอไปว่า nixpkgs เป็น registry entry, local path หรือเป้าหมายอย่างอื่น
  • nix flake show เป็นคำสั่งที่แสดงทุกสิ่งที่ flake มีให้ในมุมมองแบบ tree
  • Guix มี guix search สำหรับแพ็กเกจและ guix system search สำหรับ services แต่ไม่มีคำสั่งเทียบเท่าที่แสดงทุกสิ่งที่โปรเจ็กต์หรือ repository ใด ๆ มีให้ และต้องเปิดดูไฟล์ Scheme เอง
  • Flakes เด่นด้าน discoverability เพราะ nix flake show แสดงสิ่งที่ project มีให้อย่างสม่ำเสมอในมุมมองเดียว
  • โปรเจ็กต์ Guix มีลักษณะ ad-hoc มากกว่า โดยต้องรู้ว่าจะดูไฟล์ไหน และไม่มี standard single-entry-point file
  • Guix มีความยืดหยุ่นสูงเพราะทุกอย่างเป็น Scheme จึงสามารถนิยามและประกอบสิ่งต่าง ๆ ได้ตามต้องการโดยไม่ต้องมี schema

โมเดลแพ็กเกจและการเขียนกราฟใหม่

  • ใน Nix แพ็กเกจคือฟังก์ชันที่คืนค่า derivation ผ่านการเรียก stdenv.mkDerivation { ... } และผลลัพธ์คือ attribute set แบบทึบแสง
  • ใน Guix แพ็กเกจคือเรคอร์ด <package> ซึ่งเป็นโครงสร้างข้อมูลแบบโปร่งใสที่มีฟิลด์มีชื่อ จึงสามารถตรวจสอบ แปลง และประกอบได้ด้วยโพรซีเยอร์ Scheme มาตรฐาน
  • เนื่องจากคำจำกัดความแพ็กเกจของ Guix เป็น transparent records ไม่ใช่ opaque functions จึงสามารถทำ inspect และ transform แบบเป็นโปรแกรมได้โดยไม่ต้องมีเครื่องมือพิเศษ
  • ใน Guix แพ็กเกจเป็นข้อมูล จึงทำ graph rewrites ได้ง่าย
  • ใน Guix สามารถใช้ package-input-rewriting เพื่อไล่ตรวจทั้งกราฟการพึ่งพาและแสดงการแทนที่ perl ด้วย perl-minimal ได้
  • คีย์เวิร์ด inherit ของ Guix ใช้นิยามแพ็กเกจใหม่โดยสืบทอดทุกฟิลด์ของ coreutils แล้วเขียนทับเฉพาะฟิลด์ที่ระบุ
  • Nix มี overlays สำหรับจุดประสงค์คล้ายกัน แต่เนื่องจากอินเทอร์เฟซแบบฟังก์ชันทึบแสง จึงตรวจสอบและแปลงได้ยากกว่า ทำให้ใช้งานได้ด้อยกว่า

การอัปเดตความปลอดภัย การบูตสแตรป และการยืนยันตัวตน

  • grafting ของ Guix ช่วยให้นำการอัปเดตความปลอดภัยไปใช้กับต้นไม้การพึ่งพาได้โดยไม่ต้องคอมไพล์แพ็กเกจที่พึ่งพาทั้งหมดใหม่
  • เมื่อไลบรารีระดับล่างอย่าง glibc มีช่องโหว่ Guix สามารถเขียนพาธในสตอร์ใหม่เพื่อแทนที่ด้วยเวอร์ชันที่แก้ไขแล้วได้
  • Nix จะคอมไพล์ทุกอย่างใหม่ในกรณีอัปเดตความปลอดภัย และในต้นไม้การพึ่งพาขนาดใหญ่ เวลาคอมไพล์อาจต่างกันเป็นหลายชั่วโมง
  • Guix ให้ความสำคัญอย่างมากกับ การบูตสแตรปจากซอร์ส และสามารถสร้างทั้งระบบจากฐานความเชื่อถือขนาดเล็กได้
  • เชนการบูตสแตรปของ Guix เริ่มจาก hex assembler ขนาดราว 500 ไบต์ ต่อด้วยคอมไพเลอร์ C mes ที่เขียนด้วย Scheme, tcc และ GNU toolchain เต็มรูปแบบ
  • โปรเจ็กต์ bootstrappable builds ครอบคลุมรายละเอียดทั้งหมดของการบูตสแตรปจากซอร์สเต็มรูปแบบ
  • Nix พึ่งพา binary seeds มากกว่า Guix
  • หากไม่สามารถตรวจสอบเชนการบูตสแตรปได้ ก็ไม่อาจยืนยันได้อย่างสมบูรณ์ว่าระบบถูกสร้างจากซอร์สที่ตั้งใจจริงหรือไม่ ดังนั้นการบูตสแตรปจากซอร์สเต็มรูปแบบจึงสำคัญต่อความเชื่อถือและความสามารถในการตรวจสอบ
  • Guix channels รองรับ การยืนยันตัวตนด้วยคริปโตกราฟี เป็นค่าเริ่มต้น
  • Guix channel จะระบุ “introduction” ที่ประกอบด้วยคอมมิตเฉพาะและลายเซ็น Ed25519 ของมัน และ Guix จะตรวจสอบเชนลายเซ็นทั้งหมดตั้งแต่ introduction นั้นจนถึงคอมมิตปัจจุบัน
  • Flakes ใช้ HTTPS และโครงสร้างพื้นฐานของ GitHub เป็นโมเดลความเชื่อถือ ซึ่งเป็นโมเดลความปลอดภัยที่ต่างจากการยืนยันตัวตน channel ด้วย Ed25519 ของ Guix

ความสอดคล้องหลักในตารางสรุป

  • การประกาศการพึ่งพา: Flakes ใช้ inputs ใน flake.nix ส่วน Guix ใช้ channels.scm และ .guix-channel
  • การตรึงเวอร์ชันการพึ่งพา: Flakes ใช้ flake.lock แบบอัตโนมัติรายโปรเจ็กต์ ส่วน Guix ใช้ guix describe แบบอัตโนมัติรายผู้ใช้ และ channels.scm ที่ระบุคอมมิตเองแบบรายโปรเจ็กต์
  • การประเมินแบบบริสุทธิ์ถูกบังคับใช้ในโหมด flake และใน Guix เป็นคุณสมบัติที่มีอยู่โดยการออกแบบ
  • สคีมาของเอาต์พุต: Flakes ใช้ attrset แบบมีโครงสร้างของ outputs ส่วน Guix ใช้ Scheme records แบบ ad-hoc
  • สภาพแวดล้อมพัฒนา: Flakes ใช้ devShells และ nix develop ส่วน Guix ใช้ manifest.scm และ guix shell
  • การกำหนดค่าระบบ: Flakes ใช้ nixosConfigurations และระบบโมดูล ส่วน Guix ใช้ operating-system และ service DAG
  • การทำซ้ำได้ด้วยคำสั่งเดียว: Flakes ใช้ nix build github:foo/bar ส่วน Guix ใช้รูปแบบ guix time-machine -C channels.scm -- build
  • การตรึงเวอร์ชันรายโปรเจ็กต์: Flakes จัดการให้อัตโนมัติด้วย flake.lock ส่วน Guix จัดการด้วยตนเองผ่าน channels.scm ที่มีคอมมิต
  • ความสามารถในการสำรวจ: Flakes ใช้ nix flake show ส่วน Guix พึ่งการตรวจสอบโมดูล Scheme
  • โมเดลแพ็กเกจ: Flakes/Nix เป็นฟังก์ชันทึบแสง ส่วน Guix เป็นเรคอร์ดแบบโปร่งใส
  • init system: Nix ใช้ systemd ส่วน Guix ใช้ GNU Shepherd
  • การอัปเดตความปลอดภัย: Nix คอมไพล์ใหม่ทั้งหมด ส่วน Guix ใช้ grafting ที่รวดเร็ว
  • ความเชื่อถือในการบูตสแตรป: Nix อิงกับ binary seeds ส่วน Guix อิงกับการบูตสแตรปจากซอร์สเต็มรูปแบบ
  • การอัปเดตแบบยืนยันตัวตนแล้ว: Flakes อิงความเชื่อถือจาก HTTPS/GitHub ส่วน Guix ใช้การยืนยันตัวตน channel ด้วย Ed25519
  • การรองรับ FHS: Nix มี buildFHSUserEnv ส่วน Guix มี --emulate-fhs
  • การรองรับนอก Linux: Nix มี nix-darwin สำหรับ macOS ส่วน Guix มีการจัดระเบียบรอบ GNU Hurd
  • การเป็นซอฟต์แวร์เสรีล้วนหรือไม่: Nix ไม่ได้จำกัดเฉพาะซอฟต์แวร์เสรีและปรับแต่งได้ ส่วน Guix ปฏิบัติตาม FSDG

บทสรุป

  • Flakes และ Guix แก้ปัญหาประเภทเดียวกันคือความสามารถในการทำซ้ำ การจัดการการพึ่งพา และการประกาศระบบ แต่ใช้ปรัชญาสถาปัตยกรรมที่ต่างกัน
  • Flakes ใกล้เคียงกับการเป็นฟีเจอร์เดียวที่รวมหนึ่งไฟล์ หนึ่งสคีมา หนึ่งไฟล์ล็อก และหนึ่งชุดธรรมเนียมปฏิบัติ
  • Guix คือการผสมผสานของเครื่องมือที่ตั้งฉากต่อกัน เช่น channels สำหรับการแจกจ่าย, manifests สำหรับสภาพแวดล้อม, operating-system สำหรับการกำหนดค่า, guix time-machine สำหรับการทำซ้ำได้ และ Scheme records สำหรับโครงสร้างอื่น ๆ
  • หากชอบวิธีมาตรฐานแบบเดียว ไฟล์จุดเริ่มต้นเพียงไฟล์เดียว สคีมาเอาต์พุตแบบเดียว และรูปแบบไฟล์ล็อกแบบเดียว Flakes จะเหมาะโดยธรรมชาติ
  • หากชอบแนวทางที่นำเครื่องมือเล็ก ๆ และเป็นอิสระมาประกอบกัน เพื่อให้แต่ละเครื่องมือทำสิ่งเดียวได้ดีตามปรัชญา Unix กับการจัดการแพ็กเกจ Guix จะเหมาะกว่า
  • ทั้งสองระบบนิเวศพัฒนาขึ้นโดยมีแนวคิดร่วมกันว่า การจัดการแพ็กเกจควรเป็นแบบฟังก์ชัน แบบประกาศ และทำซ้ำได้ โดยผลักดันแนวคิดเดียวกันผ่านการนำไปใช้ที่ต่างกัน

1 ความคิดเห็น

 
GN⁺ 5 시간 전
ความคิดเห็นจาก Lobste.rs
  • เว็บนี้อ่านบนมือถือแล้วอึดอัดมาก: ตัวอักษรเล็กไปหน่อย และคอยรบกวนทุกครั้งที่เลื่อน
    หลังจากเทียบส่วนแรกแล้วก็อ่านต่อไม่ไหว เพราะมัน เด้งกลับขึ้นไปที่สารบัญ ตลอด

    • อ่านไม่ได้เลย มันเด้งไปเด้งมาเหมือนโยโย่ นี่เป็นหนึ่งในบทความที่ช่วงนี้ฉันอยากอ่านมากจริง ๆ เลยยิ่งผิดหวัง เพราะเป็น ประสบการณ์การอ่านที่น่าหงุดหงิด ที่สุดเท่าที่เคยเจอ
    • บนเดสก์ท็อปก็เป็นเหมือนกัน ไร้สาระมาก ดูเหมือนจงใจทำลายการเข้าถึงเสียด้วยซ้ำ
  • ถึงจะอ่านบทความแล้ว ฉันก็ยังไม่ค่อยเข้าใจว่าจะระบุและตรึง dependencies ของโปรเจกต์อย่างไร ดูเหมือนว่าถ้าจะ deploy และแชร์ ก็ต้องไปหา commit hash ของแต่ละ transitive dependency มาใส่ใน channels.scm เองด้วยมือ
    time-machine ดูเหมือนจะใช้ได้เฉพาะกับชุดแพ็กเกจ Guix ไม่ได้ใช้กับ dependency นอก tree
    ในฝั่ง Nix ก็สามารถรันโค้ดของ nixpkgs ณ commit เก่าได้ค่อนข้างง่าย เช่น nix run github:nixos/nixpkgs/<commit hash>#<package>
    จุดที่ Guix แปลกคือมันไม่ได้แยก เวอร์ชันของชุดแพ็กเกจ ออกจาก เวอร์ชันของตัวจัดการแพ็กเกจ ถ้าจะรันแพ็กเกจเก่า ก็ต้องรัน Guix รุ่นเก่าด้วย ซึ่งฉันไม่ค่อยเข้าใจว่าทำไมเราต้องการแบบนั้น
    ในบทความบอกว่า flakes ต้องไปหา commit มากำหนดเอง แต่ถัดมากลับยกตัวอย่างคำสั่ง Guix ที่ก็ต้องระบุ commit เหมือนกัน ใน Nix flake เองก็ override เวอร์ชันของ nixpkgs ได้ด้วย --override-input แต่ก็ค่อนข้างรก และนั่นก็เป็นหนึ่งในจุดที่ unflake พยายามแก้

    • ฉันใช้ Guix น้อยกว่าคนเขียน แต่พออ่านเอกสารมาบ้าง เลยขอตอบบางข้อ
      ปกติจะพัฒนาใน environment ของ guix shell ก่อน แล้วพอถึงขั้นจะแชร์ค่อยใช้ guix describe -f channels > channels.scm เพื่อบันทึก commit hash ทั้งหมดลงใน channels.scm
      จากเอกสาร declaring channel dependencies ดูเหมือนจะระบุ commit ของ dependency ได้ แต่ไม่เห็นมีตัวเลือกตรวจสอบว่า dependency นั้นถ้าไปพึ่ง dependency อื่นต่อ จะถูกตรึงไว้ที่ commit เฉพาะหรือไม่
      รูปแบบ --commit= ของ time-machine ใช้กับ Guix channel แต่ใช้ -C เพื่อโหลด channel เพิ่มเติมจากไฟล์ได้
      ข้อดีคือถึงจะมี การเปลี่ยนแปลงที่ทำให้เข้ากันไม่ได้ ระหว่างตัวจัดการแพ็กเกจกับ record ของแพ็กเกจ ก็ยังรักษาประวัติและความทำซ้ำได้
    • ถ้าจะทำเรื่องนี้ ก็สร้างดัชนีย้อนกลับของ nixpkgs ได้: เช่น commit X ถึง Y ของ nixpkgs มีแพ็กเกจเวอร์ชัน A อยู่
      แต่ต้อง checkout nixpkgs ทุก commit เลย ทำให้ต้นทุนการสร้างครั้งแรกสูงมาก พอสร้างเสร็จแล้วต้นทุนการดูแลดัชนีน่าจะต่ำ
  • ฉันแจ้ง coopi เรื่องปัญหาของเว็บกับกระทู้นี้ไปแล้ว หวังว่าจะมีการแก้เร็ว ๆ นี้
    ในฐานะคนที่เอนเอียงมาทาง Guix เต็มตัว ฉันก็เห็นด้วยกับที่ coopi พูดว่า Guix น่าจะมี ไฟล์/ไดเรกทอรีมาตรฐาน แบบ flake.nix เดียวหรือไดเรกทอรี nix เดียวที่รวมทุกอย่างไว้ได้เหมือน Nix เพียงแต่การ import Scheme module ต้องระบุ path ให้ถูก เลยอาจทำไม่ได้
    สำหรับโพสต์ใน Lobsters นี้ เนื้อหาที่ผู้เขียนพูดถึงก็ดูเหมือนใช้แท็กแค่ nix กับ lisp ก็พอแล้ว

    • ฉันยังไม่ค่อยเชื่อว่าควรเอา linux ออก นั่นคือเคอร์เนลเดียวที่ทั้งคู่มีร่วมกันนี่นา :P แต่ unix อาจไม่เข้าก็ได้อย่างที่ว่า
    • ถ้ามี ชนิดข้อมูล channel แบบมีโครงสร้าง ที่ช่วยอนุมานได้อัตโนมัติว่า channel นั้นมีอะไรให้บ้างแบบ flakes ก็คงดี
      เช่นประมาณนี้:
      (channel  
        (operating-systems  
          (list my-vm))  
        (services  
          (list my-system-service)))  
      
      และก็น่าจะมีคำสั่ง guix channel ที่ช่วยสร้างและจัดการ channel ใหม่ด้วย
  • สงสัยว่าใน Guix มีฟีเจอร์แบบ .inputs.nixpkgs.follows ของ Nix flake ไหม คือเอาไว้ override ค่าที่ตรึงไว้ของ transitive dependency
    อีกอย่าง คำอธิบาย Guix ของผู้เขียนหลายส่วนทำให้นึกถึง Nix ก่อนยุค flakes: ไม่มีจุดเข้าใช้งานมาตรฐาน ใช้ channels และอะไรทำนองนั้น เพียงแต่ใน Guix มี type system กับภาษาโปรแกรมจริง ๆ เลยทำให้ pattern แบบเดียวกันเจ็บปวดน้อยกว่า รู้สึกเหมือนประวัติศาสตร์ทางเลือกที่ Nix คงเป็นถ้ามันดีกว่านี้หรือใช้ภาษาอื่น

    • ฟีเจอร์นั้นเอาไว้ใช้ทำอะไร?
  • ปัญหาด้านการใช้งานที่คนอื่นชี้ไว้ทำให้ฉันยังลังเลที่จะแนะนำ ฉันใช้ NoScript ที่ปิด JavaScript เป็นค่าเริ่มต้น เลยไม่ทันสังเกต
    ถึงอย่างนั้น บทความนี้ก็มาถูกจังหวะพอดี เพราะที่บริษัทกำลังไปทางใช้ Nix เยอะขึ้น และเราก็กำลังเจอเรื่อง flakes อยู่บ้าง คำอธิบายในบทความนี้ชัดกว่าสิ่งที่ฉันเคยอ่านมาก

    • Coopi บอกว่าเขาพยายามทำตามแนวคิด progressive enhancement ให้ยังอ่านได้แม้ไม่มี JavaScript หรือ CSS แม้ฝั่ง JavaScript จะพังไป แต่แนวคิดนั้นอย่างน้อยก็ยังพอใช้ได้อยู่ และตอนนี้ JavaScript ก็น่าจะถูกแก้แล้ว
  • คำตอบจาก Coopi: เช้านี้มีการเปลี่ยนแปลงบางอย่าง แต่ติดงานเลยยังไม่ได้ทดสอบ และปรากฏว่ามี ปัญหาใน JavaScript จริง

  • เว็บนี้ใช้บน iOS มือถือไม่ได้ หน้าเหมือนจะโหลดจากด้านล่างแล้วเลื่อนกลับขึ้นไปด้านบนทันที และถ้าฉันเลื่อนลงเกินหนึ่งหน้าจอ จะมีบางอย่างถูก trigger แล้วดันกลับขึ้นไปอีก
    โหมดอ่านยังใช้ได้ แต่ highlighting กับรายละเอียดการจัดสไตล์เดิมที่จริง ๆ ก็ทำไว้ค่อนข้างดีจะหายไป

  • จะบอกว่าสิ่งที่ flakes ทำสามารถทำได้ด้วยเครื่องมือหลายตัวของ Guix ก็พูดได้อยู่ แต่ก็ควรพูดด้วยว่าใน Nix เองก็มีเครื่องมือเล็ก ๆ ที่แยกหน้าที่ชัดเจนไว้แก้ปัญหาเดียวกันนี้มานานแล้วและยังมีอยู่จนถึงตอนนี้
    สิ่งที่ flakes มอบให้คือ จุดเข้าใช้งานโปรเจกต์แบบมาตรฐาน และ ecosystem ที่เกิดขึ้นได้จากสิ่งนั้น เช่น registry ซึ่งในบทความเองก็ยอมรับว่า Guix ยังไม่มีส่วนนี้
    ผู้ใช้ Guix อาจตัดสินใจว่าไม่จำเป็นต้องมีจุดเข้าใช้งานมาตรฐาน และผู้ใช้ Nix จำนวนมากก็เคยมองแบบนั้น
    แต่การบอกว่าชุดเครื่องมือที่แยกหน้าที่กันทำสิ่งเดียวกับ flakes ได้ ฟังดูคล้ายกับการบอกว่า FreeBSD มี jail ก็เลยไม่ต้องรองรับ OCI ก็ได้ ซึ่งมองข้ามประเด็นที่ว่ามาตรฐานเป็นสิ่งที่ทำให้ ecosystem เกิดขึ้นได้
    ฉันสนใจ Guix มากและก็เคย contribute ไปนิดหน่อย เลยอยากเทียบดูว่าการ build ด้วย guix time-machine พร้อม channels.scm ทำไมถึงช้ากว่าการเปลี่ยน flake pin แล้ว evaluate Nix มากนัก ถ้าช้ากว่าแค่ประมาณ 3 เท่า เช่นจาก 5–10 วินาทีเป็น 15–30 วินาที ฉันยังรับได้ แต่จากที่เคยลอง มันห่างจากระดับนั้นมาก