1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014 Nikita Karetnikov <nikita@karetnikov.org>
3 ;;; Copyright © 2014-2015, 2017-2019, 2021-2022 Ludovic Courtès <ludo@gnu.org>
5 ;;; This file is part of GNU Guix.
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20 (define-module (test-substitute)
21 #:use-module (guix scripts substitute)
22 #:use-module (guix narinfo)
23 #:use-module (guix base64)
24 #:use-module (gcrypt hash)
25 #:use-module (guix serialization)
26 #:use-module (gcrypt pk-crypto)
27 #:use-module (guix pki)
28 #:use-module (guix config)
29 #:use-module (guix base32)
30 #:use-module ((guix store) #:select (%store-prefix))
31 #:use-module ((guix ui) #:select (guix-warning-port))
32 #:use-module ((guix utils)
33 #:select (call-with-temporary-directory
34 call-with-compressed-output-port))
35 #:use-module ((guix build utils)
36 #:select (mkdir-p delete-file-recursively dump-port))
37 #:use-module (guix tests http)
38 #:use-module (rnrs bytevectors)
39 #:use-module (rnrs io ports)
40 #:use-module (web uri)
41 #:use-module (ice-9 regex)
42 #:use-module (srfi srfi-11)
43 #:use-module (srfi srfi-26)
44 #:use-module (srfi srfi-34)
45 #:use-module (srfi srfi-35)
46 #:use-module ((srfi srfi-64) #:hide (test-error)))
48 (define-syntax-rule (test-quit name error-rx exp)
49 "Emit a test that passes when EXP throws to 'quit' with value 1, and when
50 it writes to GUIX-WARNING-PORT a messages that matches ERROR-RX."
53 (let ((error-output (open-output-string)))
54 (parameterize ((current-error-port error-output)
55 (guix-warning-port error-output))
62 (let ((message (get-output-string error-output)))
63 (->bool (string-match error-rx message))))))))))
65 (define (request-substitution item destination)
66 "Run 'guix substitute --substitute' to fetch ITEM to DESTINATION."
67 (parameterize ((guix-warning-port (current-error-port)))
68 (with-input-from-string (string-append "substitute " item " "
71 (guix-substitute "--substitute")))))
74 ;; This key is known to be in the ACL by default.
75 (call-with-input-file (string-append %config-directory "/signing-key.pub")
76 (compose string->canonical-sexp get-string-all)))
79 (call-with-input-file (string-append %config-directory "/signing-key.sec")
80 (compose string->canonical-sexp get-string-all)))
82 (define* (signature-body bv #:key (public-key %public-key))
83 "Return the signature of BV as the base64-encoded body of a narinfo's
87 (canonical-sexp->string
88 (signature-sexp (bytevector->hash-data (sha256 bv)
93 (define %wrong-public-key
94 (string->canonical-sexp "(public-key
96 (n #00E05873AC2B168760343145918E954EE9AB73C026355693B192E01EE835261AA689E9EF46642E895BCD65C648524059FC450E4BA77A68F4C52D0E39EF0CC9359709AB6AAB153B63782201871325B0FDA19CB401CD99FD0C31A91CA9000AA90A77E82B89E036FB63BC1D3961207469B3B12468977148D376F8012BB12A4B11A8F1#)
101 (define* (signature-field bv-or-str
102 #:key (version "1") (public-key %public-key))
103 "Return the 'Signature' field value of bytevector/string BV-OR-STR, using
104 PUBLIC-KEY as the signature's principal, and using VERSION as the signature
105 version identifier.."
106 (string-append version ";example.gnu.org;"
107 (signature-body (if (string? bv-or-str)
108 (string->utf8 bv-or-str)
110 #:public-key public-key)))
114 (test-begin "substitute")
116 (test-quit "not a number"
118 (narinfo-signature->canonical-sexp
119 (signature-field "foo" #:version "not a number")))
121 (test-quit "wrong version number"
122 "unsupported.*version"
123 (narinfo-signature->canonical-sexp
124 (signature-field "foo" #:version "2")))
126 (test-assert "valid narinfo-signature->canonical-sexp"
127 (canonical-sexp? (narinfo-signature->canonical-sexp (signature-field "foo"))))
131 (define %main-substitute-directory
132 ;; The place where 'call-with-narinfo' stores its data by default.
133 (uri-path (string->uri (getenv "GUIX_BINARY_SUBSTITUTE_URL"))))
135 (define %alternate-substitute-directory
137 (string-append (dirname %main-substitute-directory)
138 "/substituter-alt-data"))
140 (define %unroutable-substitute-url
141 ;; Substitute URL with an unroutable server address, as per
142 ;; <https://www.rfc-editor.org/rfc/rfc5737>.
143 "http://203.0.113.1")
147 ;; Skeleton of the narinfo used below.
148 (string-append "StorePath: " (%store-prefix)
149 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo
152 NarHash: sha256:" (bytevector->nix-base32-string
153 (sha256 (string->utf8 "Substitutable data."))) "
156 Deriver: " (%store-prefix) "/foo.drv
157 System: mips64el-linux\n"))
159 (define* (call-with-narinfo narinfo thunk
161 (narinfo-directory %main-substitute-directory))
162 "Call THUNK in a context where the directory at URL is populated with
164 (mkdir-p narinfo-directory)
165 (let ((cache-directory (string-append (getenv "XDG_CACHE_HOME")
166 "/guix/substitute/")))
169 (when (file-exists? cache-directory)
170 (delete-file-recursively cache-directory))
171 (call-with-output-file (string-append narinfo-directory
174 (format port "StoreDir: ~a\nWantMassQuery: 0\n"
176 (call-with-output-file (string-append narinfo-directory "/"
177 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
179 (cut display narinfo <>))
182 (call-with-output-file
183 (string-append narinfo-directory "/example.out")
184 (cut display "Substitutable data." <>))
185 (call-with-output-file
186 (string-append narinfo-directory "/example.nar")
188 (string-append narinfo-directory "/example.out") <>))
190 (%allow-unauthenticated-substitutes? #f))
193 (when (file-exists? cache-directory)
194 (delete-file-recursively cache-directory))))))
196 (define-syntax-rule (with-narinfo narinfo body ...)
197 (call-with-narinfo narinfo (lambda () body ...)))
199 (define-syntax-rule (with-narinfo* narinfo directory body ...)
200 (call-with-narinfo narinfo (lambda () body ...) directory))
202 ;; Transmit these options to 'guix substitute'.
203 (substitute-urls (list (getenv "GUIX_BINARY_SUBSTITUTE_URL")))
205 ;; Never use file descriptor 4, unlike what happens when invoked by the
207 (%reply-file-descriptor #f)
210 (test-equal "query narinfo without signature"
211 "" ; not substitutable
213 (with-narinfo %narinfo
215 (with-output-to-string
217 (with-input-from-string (string-append "have " (%store-prefix)
218 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
220 (guix-substitute "--query"))))))))
222 (test-equal "query narinfo with invalid hash"
223 ;; The hash in the signature differs from the hash of %NARINFO.
226 (with-narinfo (string-append %narinfo "Signature: "
227 (signature-field "different body")
230 (with-output-to-string
232 (with-input-from-string (string-append "have " (%store-prefix)
233 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
235 (guix-substitute "--query"))))))))
237 (test-equal "query narinfo with signature over nothing"
238 ;; The signature is computed over the empty string, not over the important
239 ;; parts, so the narinfo must be ignored.
242 (with-narinfo (string-append "Signature: " (signature-field "") "\n"
245 (with-output-to-string
247 (with-input-from-string (string-append "have " (%store-prefix)
248 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
250 (guix-substitute "--query"))))))))
252 (test-equal "query narinfo with signature over irrelevant bits"
253 ;; The signature is valid but it does not cover the
254 ;; StorePath/NarHash/References tuple and is thus irrelevant; the narinfo
258 (let ((prefix (string-append "StorePath: " (%store-prefix)
259 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo
261 Compression: none\n")))
262 (with-narinfo (string-append prefix
263 "Signature: " (signature-field prefix) "
264 NarHash: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
267 Deriver: " (%store-prefix) "/foo.drv
268 System: mips64el-linux\n")
270 (with-output-to-string
272 (with-input-from-string (string-append "have " (%store-prefix)
273 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
275 (guix-substitute "--query")))))))))
277 (test-equal "query narinfo with signature over relevant subset"
278 ;; The signature covers the StorePath/NarHash/References tuple, so it is
279 ;; valid; it does not cover non-normative fields, which is fine.
280 (string-append (%store-prefix) "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
282 (let ((prefix (string-append "StorePath: " (%store-prefix)
283 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo
284 NarHash: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
285 References: bar baz\n")))
286 (with-narinfo (string-append prefix
287 "Signature: " (signature-field prefix) "
291 Deriver: " (%store-prefix) "/foo.drv")
293 (with-output-to-string
295 (with-input-from-string (string-append "have " (%store-prefix)
296 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
298 (guix-substitute "--query")))))))))
300 (test-equal "query narinfo signed with authorized key"
301 (string-append (%store-prefix) "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
303 (with-narinfo (string-append %narinfo "Signature: "
304 (signature-field %narinfo)
307 (with-output-to-string
309 (with-input-from-string (string-append "have " (%store-prefix)
310 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
312 (guix-substitute "--query"))))))))
314 (test-equal "query narinfo signed with authorized key, unroutable URL first"
315 (string-append (%store-prefix) "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
317 (with-narinfo (string-append %narinfo "Signature: "
318 (signature-field %narinfo)
321 (with-output-to-string
323 (with-input-from-string (string-append "have " (%store-prefix)
324 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
326 (parameterize ((substitute-urls
327 (list %unroutable-substitute-url
328 (string-append "file://"
329 %main-substitute-directory))))
330 (guix-substitute "--query")))))))))
332 (test-equal "query narinfo signed with unauthorized key"
333 "" ; not substitutable
335 (with-narinfo (string-append %narinfo "Signature: "
338 #:public-key %wrong-public-key)
341 (with-output-to-string
343 (with-input-from-string (string-append "have " (%store-prefix)
344 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
346 (guix-substitute "--query"))))))))
348 (test-quit "substitute, no signature"
349 "no valid substitute"
350 (with-narinfo %narinfo
351 (with-input-from-string (string-append "substitute "
353 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo"
356 (guix-substitute "--substitute")))))
358 (test-quit "substitute, invalid narinfo hash"
359 "no valid substitute"
360 ;; The hash in the signature differs from the hash of %NARINFO.
361 (with-narinfo (string-append %narinfo "Signature: "
362 (signature-field "different body")
364 (with-input-from-string (string-append "substitute "
366 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo"
369 (guix-substitute "--substitute")))))
371 (test-equal "substitute, invalid hash"
372 (string-append "hash-mismatch sha256 "
373 (bytevector->nix-base32-string (sha256 #vu8())) " "
374 (let-values (((port get-hash)
375 (open-hash-port (hash-algorithm sha256)))
377 "Substitutable data."))
378 (write-file-tree "foo" port
382 (string-length content)))
385 (open-input-string content)))
387 (bytevector->nix-base32-string (get-hash)))
390 ;; Arrange so the actual data hash does not match the 'NarHash' field in the
392 (with-output-to-string
394 (let ((narinfo (string-append "StorePath: " (%store-prefix)
395 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-wrong-hash
398 NarHash: sha256:" (bytevector->nix-base32-string (sha256 #vu8())) "
401 Deriver: " (%store-prefix) "/foo.drv
402 System: mips64el-linux\n")))
403 (with-narinfo (string-append narinfo "Signature: "
404 (signature-field narinfo) "\n")
405 (call-with-temporary-directory
407 (with-input-from-string (string-append
408 "substitute " (%store-prefix)
409 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-wrong-hash "
410 directory "/wrong-hash\n")
412 (guix-substitute "--substitute"))))))))))
414 (test-quit "substitute, unauthorized key"
415 "no valid substitute"
416 (with-narinfo (string-append %narinfo "Signature: "
419 #:public-key %wrong-public-key)
421 (with-input-from-string (string-append "substitute "
423 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo"
426 (guix-substitute "--substitute")))))
428 (test-equal "substitute, authorized key"
429 '("Substitutable data." 1 #o444)
430 (with-narinfo (string-append %narinfo "Signature: "
431 (signature-field %narinfo))
435 (request-substitution (string-append (%store-prefix)
436 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
437 "substitute-retrieved")
438 (list (call-with-input-file "substitute-retrieved" get-string-all)
439 (stat:mtime (lstat "substitute-retrieved"))
440 (stat:perms (lstat "substitute-retrieved"))))
442 (false-if-exception (delete-file "substitute-retrieved"))))))
444 (test-equal "substitute, authorized key, first substitute URL is unroutable"
445 '("Substitutable data." 1 #o444)
446 (with-narinfo (string-append %narinfo "Signature: "
447 (signature-field %narinfo))
451 ;; Pick an unroutable URL as the first one. This shouldn't be a
453 (parameterize ((substitute-urls
454 (list %unroutable-substitute-url
455 (string-append "file://"
456 %main-substitute-directory))))
457 (request-substitution (string-append (%store-prefix)
458 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
459 "substitute-retrieved")
460 (list (call-with-input-file "substitute-retrieved" get-string-all)
461 (stat:mtime (lstat "substitute-retrieved"))
462 (stat:perms (lstat "substitute-retrieved")))))
464 (false-if-exception (delete-file "substitute-retrieved"))))))
466 (test-equal "substitute, unauthorized narinfo comes first"
467 "Substitutable data."
469 (string-append %narinfo "Signature: "
472 #:public-key %wrong-public-key))
473 %alternate-substitute-directory
475 (with-narinfo* (string-append %narinfo "Signature: "
476 (signature-field %narinfo))
477 %main-substitute-directory
482 ;; Remove this file so that the substitute can only be retrieved
483 ;; from %ALTERNATE-SUBSTITUTE-DIRECTORY.
484 (delete-file (string-append %main-substitute-directory
487 (parameterize ((substitute-urls
488 (map (cut string-append "file://" <>)
489 (list %alternate-substitute-directory
490 %main-substitute-directory))))
491 (request-substitution (string-append (%store-prefix)
492 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
493 "substitute-retrieved"))
494 (call-with-input-file "substitute-retrieved" get-string-all))
496 (false-if-exception (delete-file "substitute-retrieved")))))))
498 (test-equal "substitute, unsigned narinfo comes first"
499 "Substitutable data."
500 (with-narinfo* %narinfo ;not signed!
501 %alternate-substitute-directory
503 (with-narinfo* (string-append %narinfo "Signature: "
504 (signature-field %narinfo))
505 %main-substitute-directory
510 ;; Remove this file so that the substitute can only be retrieved
511 ;; from %ALTERNATE-SUBSTITUTE-DIRECTORY.
512 (delete-file (string-append %main-substitute-directory
515 (parameterize ((substitute-urls
516 (map (cut string-append "file://" <>)
517 (list %alternate-substitute-directory
518 %main-substitute-directory))))
519 (request-substitution (string-append (%store-prefix)
520 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
521 "substitute-retrieved"))
522 (call-with-input-file "substitute-retrieved" get-string-all))
524 (false-if-exception (delete-file "substitute-retrieved")))))))
526 (test-equal "substitute, first URL has narinfo but lacks nar, second URL unauthorized"
527 "Substitutable data."
529 (string-append %narinfo "Signature: "
532 #:public-key %wrong-public-key))
533 %alternate-substitute-directory
535 (with-narinfo* (string-append %narinfo "Signature: "
536 (signature-field %narinfo))
537 %main-substitute-directory
542 ;; Remove this file so that the substitute can only be retrieved
543 ;; from %ALTERNATE-SUBSTITUTE-DIRECTORY.
544 (delete-file (string-append %main-substitute-directory
547 (parameterize ((substitute-urls
548 (map (cut string-append "file://" <>)
549 (list %main-substitute-directory
550 %alternate-substitute-directory))))
551 (request-substitution (string-append (%store-prefix)
552 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
553 "substitute-retrieved"))
554 (call-with-input-file "substitute-retrieved" get-string-all))
556 (false-if-exception (delete-file "substitute-retrieved")))))))
558 (test-equal "substitute, first URL has narinfo but nar is 404, both URLs authorized"
559 "Substitutable data."
561 (string-append %narinfo "Signature: "
562 (signature-field %narinfo))
563 %main-substitute-directory
565 (with-http-server `((200 ,(string-append %narinfo "Signature: "
566 (signature-field %narinfo)))
567 (404 "Sorry, nar is missing!"))
571 (parameterize ((substitute-urls
573 (string-append "file://"
574 %main-substitute-directory))))
575 (request-substitution (string-append (%store-prefix)
576 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
577 "substitute-retrieved"))
578 (call-with-input-file "substitute-retrieved" get-string-all))
580 (false-if-exception (delete-file "substitute-retrieved")))))))
582 (test-equal "substitute, first URL has narinfo but nar is 404, one URL authorized"
583 "Substitutable data."
585 (string-append %narinfo "Signature: "
588 #:public-key %wrong-public-key))
589 %main-substitute-directory
591 (with-http-server `((200 ,(string-append %narinfo "Signature: "
594 #:public-key %wrong-public-key)))
595 (404 "Sorry, nar is missing!"))
596 (let ((url1 (%local-url)))
597 (parameterize ((%http-server-port 0))
598 (with-http-server `((200 ,(string-append %narinfo "Signature: "
599 (signature-field %narinfo)))
600 (404 "Sorry, nar is missing!"))
601 (let ((url2 (%local-url)))
605 (parameterize ((substitute-urls
607 (string-append "file://"
608 %main-substitute-directory))))
609 (request-substitution (string-append (%store-prefix)
610 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
611 "substitute-retrieved"))
612 (call-with-input-file "substitute-retrieved" get-string-all))
614 (false-if-exception (delete-file "substitute-retrieved")))))))))))
616 (test-quit "substitute, narinfo is available but nar is missing"
617 "failed to find alternative substitute"
619 (string-append %narinfo "Signature: "
622 #:public-key %wrong-public-key))
623 %main-substitute-directory
625 (with-http-server `((200 ,(string-append %narinfo "Signature: "
626 (signature-field %narinfo)))
627 (404 "Sorry, nar is missing!"))
628 (parameterize ((substitute-urls
630 (string-append "file://"
631 %main-substitute-directory))))
632 (delete-file (string-append %main-substitute-directory
634 (request-substitution (string-append (%store-prefix)
635 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
636 "substitute-retrieved")
637 (not (file-exists? "substitute-retrieved"))))))
639 (test-equal "substitute, first narinfo is unsigned and has wrong hash"
640 "Substitutable data."
641 (with-narinfo* (regexp-substitute #f
642 (string-match "NarHash: [[:graph:]]+"
646 (bytevector->nix-base32-string
647 (make-bytevector 32))
649 %alternate-substitute-directory
651 (with-narinfo* (string-append %narinfo "Signature: "
652 (signature-field %narinfo))
653 %main-substitute-directory
658 ;; This time remove the file so that the substitute can only be
659 ;; retrieved from %MAIN-SUBSTITUTE-DIRECTORY.
660 (delete-file (string-append %alternate-substitute-directory
663 (parameterize ((substitute-urls
664 (map (cut string-append "file://" <>)
665 (list %alternate-substitute-directory
666 %main-substitute-directory))))
667 (request-substitution (string-append (%store-prefix)
668 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
669 "substitute-retrieved"))
670 (call-with-input-file "substitute-retrieved" get-string-all))
672 (false-if-exception (delete-file "substitute-retrieved")))))))
674 (test-equal "substitute, first narinfo is unsigned and has wrong refs"
675 "Substitutable data."
676 (with-narinfo* (regexp-substitute #f
677 (string-match "References: ([^\n]+)\n"
679 'pre "References: " 1
680 " wrong set of references\n"
682 %alternate-substitute-directory
684 (with-narinfo* (string-append %narinfo "Signature: "
685 (signature-field %narinfo))
686 %main-substitute-directory
691 ;; This time remove the file so that the substitute can only be
692 ;; retrieved from %MAIN-SUBSTITUTE-DIRECTORY.
693 (delete-file (string-append %alternate-substitute-directory
696 (parameterize ((substitute-urls
697 (map (cut string-append "file://" <>)
698 (list %alternate-substitute-directory
699 %main-substitute-directory))))
700 (request-substitution (string-append (%store-prefix)
701 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
702 "substitute-retrieved"))
703 (call-with-input-file "substitute-retrieved" get-string-all))
705 (false-if-exception (delete-file "substitute-retrieved")))))))
707 (test-quit "substitute, two invalid narinfos"
708 "no valid substitute"
709 (with-narinfo* %narinfo ;not signed
710 %alternate-substitute-directory
712 (with-narinfo* (string-append %narinfo "Signature: " ;unauthorized
715 #:public-key %wrong-public-key))
716 %main-substitute-directory
718 (with-input-from-string (string-append "substitute "
720 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo"
721 " substitute-retrieved\n")
723 (guix-substitute "--substitute"))))))
725 (test-equal "substitute, narinfo with several URLs"
726 "Substitutable data."
727 (let ((narinfo (string-append "StorePath: " (%store-prefix)
728 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo
735 NarHash: sha256:" (bytevector->nix-base32-string
736 (sha256 (string->utf8 "Substitutable data."))) "
739 Deriver: " (%store-prefix) "/foo.drv
740 System: mips64el-linux\n")))
741 (with-narinfo (string-append narinfo "Signature: "
742 (signature-field narinfo))
746 (define (compress input output compression)
747 (call-with-output-file output
749 (call-with-compressed-output-port compression port
751 (call-with-input-file input
753 (dump-port input port))))))))
755 (let ((nar (string-append %main-substitute-directory
757 (compress nar (string-append nar ".gz") 'gzip)
758 (compress nar (string-append nar ".lz") 'lzip))
760 (parameterize ((substitute-urls
761 (list (string-append "file://"
762 %main-substitute-directory))))
763 (request-substitution (string-append (%store-prefix)
764 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo")
765 "substitute-retrieved"))
766 (call-with-input-file "substitute-retrieved" get-string-all))
768 (false-if-exception (delete-file "substitute-retrieved")))))))
770 (test-end "substitute")
773 ;;; eval: (put 'with-narinfo 'scheme-indent-function 1)
774 ;;; eval: (put 'with-narinfo* 'scheme-indent-function 2)
775 ;;; eval: (put 'test-quit 'scheme-indent-function 2)