diff --git a/docs/GUIX_DIFFERENCES.md b/docs/GUIX_DIFFERENCES.md index 02d4ecc..cdfb19d 100644 --- a/docs/GUIX_DIFFERENCES.md +++ b/docs/GUIX_DIFFERENCES.md @@ -197,6 +197,51 @@ So if you come from Guix, assume that Fruix now has: - explicit generation metadata roots - a real but still modest installed-system switch/rollback UX +## 7. Fruix keeps Guix-like store semantics, but not Guix/Nix hash-prefix machinery exactly + +Fruix still uses immutable store paths under: + +- `/frx/store` + +and it still treats a store path as a deployment identity boundary. + +But Fruix now intentionally differs from Guix/Nix in how the visible store-path prefix is constructed. + +Current Fruix policy is: + +- centralize store-path naming behind shared helpers +- hash a small semantic identity record rather than copying Nix's historical path-hash formula exactly +- include at least: + - object kind + - logical/display name + - output name + - payload or manifest identity + - hash-scheme version marker +- truncate the visible SHA-256 prefix to **160 bits** +- render that visible prefix as **40 hex characters** + +Why Fruix does this instead of copying Guix/Nix exactly: + +- the main goal was shorter store prefixes, not Nix compatibility for its own sake +- distinct outputs should have distinct identities because `out`, `lib`, `debug`, and `doc` are semantically different artifacts +- Fruix wanted one central policy point that can be swapped later without touching every materializer again +- Fruix did **not** want to inherit Nix's legacy details unless they provide clear value here + - custom base32 alphabet and bit ordering + - compressed/XOR-folded path hashes + - exact historical `output:out` / `source` path-hash conventions + +So compared with Guix: + +- the important semantic property is the same: + - different store objects should get different immutable identities +- the exact printable prefix algorithm is intentionally simpler in Fruix today + +For Guix-familiar operators, the practical takeaway is: + +- still think of `/frx/store/...` paths as immutable deployment identities +- do **not** assume Fruix store prefixes are byte-for-byte comparable to Guix/Nix ones +- expect Fruix to prefer a simpler, centralized naming policy unless exact Guix/Nix behavior becomes necessary later + ## Where Fruix is intentionally trying to improve on Guix's representation Fruix is not trying to improve on Guix's core semantics. Guix already got those right. diff --git a/modules/fruix/system/freebsd/build.scm b/modules/fruix/system/freebsd/build.scm index 270bfc9..253ed8d 100644 --- a/modules/fruix/system/freebsd/build.scm +++ b/modules/fruix/system/freebsd/build.scm @@ -364,11 +364,11 @@ (cached (hash-ref cache cache-key #f))) (if cached cached - (let* ((hash (sha256-string manifest)) - (output-path (string-append store-dir "/" hash "-" - (freebsd-package-name prepared-package) - "-" - (freebsd-package-version prepared-package)))) + (let* ((display-name (string-append (freebsd-package-name prepared-package) + "-" + (freebsd-package-version prepared-package))) + (output-path (make-store-path store-dir display-name manifest + #:kind 'freebsd-package))) (unless (file-exists? output-path) (case (freebsd-package-build-system prepared-package) ((copy-build-system) @@ -454,8 +454,9 @@ (define* (materialize-prefix source-path name version store-dir #:key (extra-files '())) (let* ((manifest (prefix-manifest-string source-path extra-files)) - (hash (sha256-string manifest)) - (output-path (string-append store-dir "/" hash "-" name "-" version))) + (display-name (string-append name "-" version)) + (output-path (make-store-path store-dir display-name manifest + #:kind 'prefix))) (unless (file-exists? output-path) (mkdir-p output-path) (for-each (lambda (entry) diff --git a/modules/fruix/system/freebsd/media.scm b/modules/fruix/system/freebsd/media.scm index ecf7b44..de7115f 100644 --- a/modules/fruix/system/freebsd/media.scm +++ b/modules/fruix/system/freebsd/media.scm @@ -172,9 +172,10 @@ "\n") "\nreferences=\n" (string-join references "\n"))) - (hash (sha256-string manifest)) - (closure-path (string-append store-dir "/" hash "-fruix-system-" - (operating-system-host-name os)))) + (display-name (string-append "fruix-system-" + (operating-system-host-name os))) + (closure-path (make-store-path store-dir display-name manifest + #:kind 'operating-system))) (unless (file-exists? closure-path) (mkdir-p closure-path) (mkdir-p (string-append closure-path "/boot/kernel")) @@ -870,9 +871,10 @@ "\nstore-items=\n" (string-join store-items "\n") "\n")) - (hash (sha256-string manifest)) - (image-store-path (string-append store-dir "/" hash "-fruix-bhyve-image-" - (operating-system-host-name os))) + (display-name (string-append "fruix-bhyve-image-" + (operating-system-host-name os))) + (image-store-path (make-store-path store-dir display-name manifest + #:kind 'bhyve-image)) (disk-image (string-append image-store-path "/disk.img")) (esp-image (string-append image-store-path "/esp.img")) (root-image (string-append image-store-path "/root.ufs"))) @@ -1024,9 +1026,10 @@ "\ninstall-metadata=\n" (object->string install-metadata) "\n")) - (hash (sha256-string manifest)) - (image-store-path (string-append store-dir "/" hash "-fruix-installer-image-" - (operating-system-host-name installer-os))) + (display-name (string-append "fruix-installer-image-" + (operating-system-host-name installer-os))) + (image-store-path (make-store-path store-dir display-name manifest + #:kind 'installer-image)) (disk-image (string-append image-store-path "/disk.img")) (esp-image (string-append image-store-path "/esp.img")) (root-image (string-append image-store-path "/root.ufs"))) @@ -1329,9 +1332,10 @@ "\ninstall-metadata=\n" (object->string install-metadata) "\n")) - (hash (sha256-string manifest)) - (iso-store-path (string-append store-dir "/" hash "-fruix-installer-iso-" - (operating-system-host-name installer-os))) + (display-name (string-append "fruix-installer-iso-" + (operating-system-host-name installer-os))) + (iso-store-path (make-store-path store-dir display-name manifest + #:kind 'installer-iso)) (iso-image (string-append iso-store-path "/installer.iso")) (boot-efi-image (string-append iso-store-path "/efiboot.img")) (root-image (string-append iso-store-path "/root.img"))) diff --git a/modules/fruix/system/freebsd/source.scm b/modules/fruix/system/freebsd/source.scm index a508e8e..ef6b36e 100644 --- a/modules/fruix/system/freebsd/source.scm +++ b/modules/fruix/system/freebsd/source.scm @@ -150,9 +150,10 @@ (effective-source (assoc-ref resolution 'effective-source)) (identity (assoc-ref resolution 'identity)) (manifest (freebsd-source-manifest source effective-source identity)) - (hash (sha256-string manifest)) - (output-path (string-append store-dir "/" hash "-freebsd-source-" - (safe-name-fragment (freebsd-source-name source)))) + (display-name (string-append "freebsd-source-" + (safe-name-fragment (freebsd-source-name source)))) + (output-path (make-store-path store-dir display-name manifest + #:kind 'freebsd-source)) (info-file (string-append output-path "/.freebsd-source-info.scm")) (cache-path (assoc-ref resolution 'cache-path)) (populate-tree (assoc-ref resolution 'populate-tree))) diff --git a/modules/fruix/system/freebsd/utils.scm b/modules/fruix/system/freebsd/utils.scm index ab13445..c5ac71d 100644 --- a/modules/fruix/system/freebsd/utils.scm +++ b/modules/fruix/system/freebsd/utils.scm @@ -14,6 +14,8 @@ safe-command-output write-file sha256-string + store-hash-string + make-store-path file-hash directory-entries path-signature @@ -68,6 +70,40 @@ (write-file tmp text) (command-output "sha256" "-q" tmp))) +(define store-hash-visible-length 40) +(define store-hash-scheme-version "1") + +(define (store-identity-field value) + (cond ((symbol? value) + (symbol->string value)) + ((string? value) + value) + (else + (object->string value)))) + +(define* (store-hash-string payload #:key (kind 'item) name (output "out")) + (let* ((identity `((scheme . "fruix-store-path") + (version . ,store-hash-scheme-version) + (kind . ,(store-identity-field kind)) + (name . ,(store-identity-field (or name ""))) + (output . ,(store-identity-field output)) + (payload . ,payload))) + (digest (sha256-string (object->string identity)))) + (string-take digest store-hash-visible-length))) + +(define* (make-store-path store-dir display-name payload + #:key + (kind 'item) + name + (output "out")) + (string-append store-dir "/" + (store-hash-string payload + #:kind kind + #:name (or name display-name) + #:output output) + "-" + display-name)) + (define (file-hash path) (command-output "sha256" "-q" path))