gnu: Add go-github-com-emersion-go-msgauth.
[jackhill/guix/guix.git] / tests / pack.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
4 ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
5 ;;;
6 ;;; This file is part of GNU Guix.
7 ;;;
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
12 ;;;
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
17 ;;;
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21 (define-module (test-pack)
22 #:use-module (guix scripts pack)
23 #:use-module (guix store)
24 #:use-module (guix derivations)
25 #:use-module (guix profiles)
26 #:use-module (guix packages)
27 #:use-module (guix monads)
28 #:use-module (guix tests)
29 #:use-module (guix gexp)
30 #:use-module (guix modules)
31 #:use-module (gnu packages)
32 #:use-module ((gnu packages base) #:select (glibc-utf8-locales))
33 #:use-module (gnu packages bootstrap)
34 #:use-module ((gnu packages compression) #:select (squashfs-tools))
35 #:use-module ((gnu packages debian) #:select (dpkg))
36 #:use-module ((gnu packages guile) #:select (guile-sqlite3))
37 #:use-module ((gnu packages gnupg) #:select (guile-gcrypt))
38 #:use-module (srfi srfi-64))
39
40 (define %store
41 (open-connection-for-tests))
42
43 ;; Globally disable grafts because they can trigger early builds.
44 (%graft? #f)
45
46 (define-syntax-rule (test-assertm name store exp)
47 (test-assert name
48 (let ((guile (package-derivation store %bootstrap-guile)))
49 (run-with-store store exp
50 #:guile-for-build guile))))
51
52 (define %gzip-compressor
53 ;; Compressor that uses the bootstrap 'gzip'.
54 ((@ (guix scripts pack) compressor) "gzip"
55 ".gz"
56 #~(list #+(file-append %bootstrap-coreutils&co "/bin/gzip") "-6n")))
57
58 (define %tar-bootstrap %bootstrap-coreutils&co)
59
60 (define %ar-bootstrap %bootstrap-binutils)
61
62 \f
63 (test-begin "pack")
64
65 (unless (network-reachable?) (test-skip 1))
66 (test-assertm "self-contained-tarball" %store
67 (mlet* %store-monad
68 ((profile -> (profile
69 (content (packages->manifest (list %bootstrap-guile)))
70 (hooks '())
71 (locales? #f)))
72 (tarball (self-contained-tarball "pack" profile
73 #:symlinks '(("/bin/Guile"
74 -> "bin/guile"))
75 #:compressor %gzip-compressor
76 #:archiver %tar-bootstrap))
77 (check (gexp->derivation
78 "check-tarball"
79 (with-imported-modules '((guix build utils))
80 #~(begin
81 (use-modules (guix build utils)
82 (srfi srfi-1))
83
84 (define store
85 ;; The unpacked store.
86 (string-append "." (%store-directory) "/"))
87
88 (define (canonical? file)
89 ;; Return #t if FILE is read-only and its mtime is 1.
90 (let ((st (lstat file)))
91 (or (not (string-prefix? store file))
92 (eq? 'symlink (stat:type st))
93 (and (= 1 (stat:mtime st))
94 (zero? (logand #o222
95 (stat:mode st)))))))
96
97 (define bin
98 (string-append "." #$profile "/bin"))
99
100 (setenv "PATH"
101 (string-append #$%tar-bootstrap "/bin"))
102 (system* "tar" "xvf" #$tarball)
103 (mkdir #$output)
104 (exit
105 (and (file-exists? (string-append bin "/guile"))
106 (file-exists? store)
107 (every canonical?
108 (find-files "." (const #t)
109 #:directories? #t))
110 (string=? (string-append #$%bootstrap-guile "/bin")
111 (readlink bin))
112 (string=? (string-append ".." #$profile
113 "/bin/guile")
114 (readlink "bin/Guile")))))))))
115 (built-derivations (list check))))
116
117 ;; The following test needs guile-sqlite3, libgcrypt, etc. as a consequence of
118 ;; commit c45477d2a1a651485feede20fe0f3d15aec48b39 and related changes. Thus,
119 ;; run it on the user's store, if it's available, on the grounds that these
120 ;; dependencies may be already there, or we can get substitutes or build them
121 ;; quite inexpensively; see <https://bugs.gnu.org/32184>.
122
123 (with-external-store store
124 (unless store (test-skip 1))
125 (test-assertm "self-contained-tarball + localstatedir" store
126 (mlet* %store-monad
127 ((guile (set-guile-for-build (default-guile)))
128 (profile (profile-derivation (packages->manifest
129 (list %bootstrap-guile))
130 #:hooks '()
131 #:locales? #f))
132 (tarball (self-contained-tarball "tar-pack" profile
133 #:localstatedir? #t))
134 (check (gexp->derivation
135 "check-tarball"
136 #~(let ((bin (string-append "." #$profile "/bin")))
137 (setenv "PATH"
138 (string-append #$%tar-bootstrap "/bin"))
139 (system* "tar" "xvf" #$tarball)
140 (mkdir #$output)
141 (exit
142 (and (file-exists? "var/guix/db/db.sqlite")
143 (string=? (string-append #$%bootstrap-guile "/bin")
144 (readlink bin))))))))
145 (built-derivations (list check))))
146
147 (unless store (test-skip 1))
148 (test-assertm "self-contained-tarball + localstatedir, UTF-8 file names" store
149 (mlet* %store-monad
150 ((guile (set-guile-for-build (default-guile)))
151 (tree (interned-file-tree
152 `("directory-with-utf8-file-names" directory
153 ("α" regular (data "alpha"))
154 ("λ" regular (data "lambda")))))
155 (tarball (self-contained-tarball "tar-pack" tree
156 #:localstatedir? #t))
157 (check (gexp->derivation
158 "check-tarball"
159 (with-extensions (list guile-sqlite3 guile-gcrypt)
160 (with-imported-modules (source-module-closure
161 '((guix store database)))
162 #~(begin
163 (use-modules (guix store database)
164 (rnrs io ports)
165 (srfi srfi-1))
166
167 (define (valid-file? basename data)
168 (define file
169 (string-append "./" #$tree "/" basename))
170
171 (string=? (call-with-input-file (pk 'file file)
172 get-string-all)
173 data))
174
175 (setenv "PATH"
176 (string-append #$%tar-bootstrap "/bin"))
177 (system* "tar" "xvf" #$tarball)
178
179 (sql-schema
180 #$(local-file (search-path %load-path
181 "guix/store/schema.sql")))
182 (with-database "var/guix/db/db.sqlite" db
183 ;; Make sure non-ASCII file names are properly
184 ;; handled.
185 (setenv "GUIX_LOCPATH"
186 #+(file-append glibc-utf8-locales
187 "/lib/locale"))
188 (setlocale LC_ALL "en_US.utf8")
189
190 (mkdir #$output)
191 (exit
192 (and (every valid-file?
193 '("α" "λ")
194 '("alpha" "lambda"))
195 (integer? (path-id db #$tree)))))))))))
196 (built-derivations (list check))))
197
198 (unless store (test-skip 1))
199 (test-assertm "docker-image + localstatedir" store
200 (mlet* %store-monad
201 ((guile (set-guile-for-build (default-guile)))
202 (profile (profile-derivation (packages->manifest
203 (list %bootstrap-guile))
204 #:hooks '()
205 #:locales? #f))
206 (tarball (docker-image "docker-pack" profile
207 #:symlinks '(("/bin/Guile" -> "bin/guile"))
208 #:localstatedir? #t))
209 (check (gexp->derivation
210 "check-tarball"
211 (with-imported-modules '((guix build utils))
212 #~(begin
213 (use-modules (guix build utils)
214 (ice-9 match))
215
216 (define bin
217 (string-append "." #$profile "/bin"))
218
219 (setenv "PATH" (string-append #$%tar-bootstrap "/bin"))
220 (mkdir "base")
221 (with-directory-excursion "base"
222 (invoke "tar" "xvf" #$tarball))
223
224 (match (find-files "base" "layer.tar")
225 ((layer)
226 (invoke "tar" "xvf" layer)))
227
228 (when
229 (and (file-exists? (string-append bin "/guile"))
230 (file-exists? "var/guix/db/db.sqlite")
231 (file-is-directory? "tmp")
232 (string=? (string-append #$%bootstrap-guile "/bin")
233 (pk 'binlink (readlink bin)))
234 (string=? (string-append #$profile "/bin/guile")
235 (pk 'guilelink (readlink "bin/Guile"))))
236 (mkdir #$output)))))))
237 (built-derivations (list check))))
238
239 (unless store (test-skip 1))
240 (test-assertm "squashfs-image + localstatedir" store
241 (mlet* %store-monad
242 ((guile (set-guile-for-build (default-guile)))
243 (profile (profile-derivation (packages->manifest
244 (list %bootstrap-guile))
245 #:hooks '()
246 #:locales? #f))
247 (image (squashfs-image "squashfs-pack" profile
248 #:symlinks '(("/bin" -> "bin"))
249 #:localstatedir? #t))
250 (check (gexp->derivation
251 "check-tarball"
252 (with-imported-modules '((guix build utils))
253 #~(begin
254 (use-modules (guix build utils)
255 (ice-9 match))
256
257 (define bin
258 (string-append "." #$profile "/bin"))
259
260 (setenv "PATH"
261 (string-append #$squashfs-tools "/bin"))
262 (invoke "unsquashfs" #$image)
263 (with-directory-excursion "squashfs-root"
264 (when (and (file-exists? (string-append bin
265 "/guile"))
266 (file-exists? "var/guix/db/db.sqlite")
267 (string=? (string-append #$%bootstrap-guile "/bin")
268 (pk 'binlink (readlink bin)))
269
270 ;; This is a relative symlink target.
271 (string=? (string-drop
272 (string-append #$profile "/bin")
273 1)
274 (pk 'guilelink (readlink "bin"))))
275 (mkdir #$output))))))))
276 (built-derivations (list check))))
277
278 (unless store (test-skip 1))
279 (test-assertm "deb archive with symlinks and control files" store
280 (mlet* %store-monad
281 ((guile (set-guile-for-build (default-guile)))
282 (profile (profile-derivation (packages->manifest
283 (list %bootstrap-guile))
284 #:hooks '()
285 #:locales? #f))
286 (deb (debian-archive
287 "deb-pack" profile
288 #:compressor %gzip-compressor
289 #:symlinks '(("/opt/gnu/bin" -> "bin"))
290 #:archiver %tar-bootstrap
291 #:extra-options
292 (list #:triggers-file
293 (plain-file "triggers"
294 "activate-noawait /usr/share/icons/hicolor\n")
295 #:postinst-file
296 (plain-file "postinst"
297 "echo running configure script\n"))))
298 (check
299 (gexp->derivation "check-deb-pack"
300 (with-imported-modules '((guix build utils))
301 #~(begin
302 (use-modules (guix build utils)
303 (ice-9 match)
304 (ice-9 popen)
305 (ice-9 rdelim)
306 (ice-9 textual-ports)
307 (rnrs base))
308
309 (setenv "PATH" (string-join
310 (list (string-append #+%tar-bootstrap "/bin")
311 (string-append #+dpkg "/bin")
312 (string-append #+%ar-bootstrap "/bin"))
313 ":"))
314
315 ;; Validate the output of 'dpkg --info'.
316 (let* ((port (open-pipe* OPEN_READ "dpkg" "--info" #$deb))
317 (info (get-string-all port))
318 (exit-val (status:exit-val (close-pipe port))))
319 (assert (zero? exit-val))
320
321 (assert (string-contains
322 info
323 (string-append "Package: "
324 #+(package-name %bootstrap-guile))))
325
326 (assert (string-contains
327 info
328 (string-append "Version: "
329 #+(package-version %bootstrap-guile)))))
330
331 ;; Sanity check .deb contents.
332 (invoke "ar" "-xv" #$deb)
333 (assert (file-exists? "debian-binary"))
334 (assert (file-exists? "data.tar.gz"))
335 (assert (file-exists? "control.tar.gz"))
336
337 ;; Verify there are no hard links in data.tar.gz, as hard
338 ;; links would cause dpkg to fail unpacking the archive.
339 (define hard-links
340 (let ((port (open-pipe* OPEN_READ "tar" "-tvf" "data.tar.gz")))
341 (let loop ((hard-links '()))
342 (match (read-line port)
343 ((? eof-object?)
344 (assert (zero? (status:exit-val (close-pipe port))))
345 hard-links)
346 (line
347 (if (string-prefix? "u" line)
348 (loop (cons line hard-links))
349 (loop hard-links)))))))
350
351 (unless (null? hard-links)
352 (error "hard links found in data.tar.gz" hard-links))
353
354 ;; Verify the presence of the control files.
355 (invoke "tar" "-xf" "control.tar.gz")
356 (assert (file-exists? "control"))
357 (assert (and (file-exists? "postinst")
358 (= #o111 ;script is executable
359 (logand #o111 (stat:perms
360 (stat "postinst"))))))
361 (assert (file-exists? "triggers"))
362
363 (mkdir #$output))))))
364 (built-derivations (list check)))))
365
366 (test-end)
367
368 ;; Local Variables:
369 ;; eval: (put 'test-assertm 'scheme-indent-function 2)
370 ;; End: