upstream: Make the 'updaters' parameter optional.
[jackhill/guix/guix.git] / guix / lint.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
3 ;;; Copyright © 2014, 2015 Eric Bavier <bavier@member.fsf.org>
4 ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
5 ;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
6 ;;; Copyright © 2016 Danny Milosavljevic <dannym+a@scratchpost.org>
7 ;;; Copyright © 2016 Hartmut Goebel <h.goebel@crazy-compilers.com>
8 ;;; Copyright © 2017 Alex Kost <alezost@gmail.com>
9 ;;; Copyright © 2017 Tobias Geerinckx-Rice <me@tobias.gr>
10 ;;; Copyright © 2017, 2018, 2020 Efraim Flashner <efraim@flashner.co.il>
11 ;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
12 ;;; Copyright © 2020 Chris Marusich <cmmarusich@gmail.com>
13 ;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
14 ;;;
15 ;;; This file is part of GNU Guix.
16 ;;;
17 ;;; GNU Guix is free software; you can redistribute it and/or modify it
18 ;;; under the terms of the GNU General Public License as published by
19 ;;; the Free Software Foundation; either version 3 of the License, or (at
20 ;;; your option) any later version.
21 ;;;
22 ;;; GNU Guix is distributed in the hope that it will be useful, but
23 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
24 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ;;; GNU General Public License for more details.
26 ;;;
27 ;;; You should have received a copy of the GNU General Public License
28 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
29
30 (define-module (guix lint)
31 #:use-module (guix store)
32 #:use-module (guix base32)
33 #:use-module (guix diagnostics)
34 #:use-module (guix download)
35 #:use-module (guix ftp-client)
36 #:use-module (guix http-client)
37 #:use-module (guix packages)
38 #:use-module (guix i18n)
39 #:use-module ((guix gexp)
40 #:select (local-file? local-file-absolute-file-name))
41 #:use-module (guix licenses)
42 #:use-module (guix records)
43 #:use-module (guix grafts)
44 #:use-module (guix upstream)
45 #:use-module (guix utils)
46 #:use-module (guix memoization)
47 #:use-module (guix profiles)
48 #:use-module (guix monads)
49 #:use-module (guix scripts)
50 #:use-module ((guix ui) #:select (texi->plain-text fill-paragraph))
51 #:use-module (guix gnu-maintenance)
52 #:use-module (guix cve)
53 #:use-module ((guix swh) #:hide (origin?))
54 #:autoload (guix git-download) (git-reference?
55 git-reference-url git-reference-commit)
56 #:use-module (guix import stackage)
57 #:use-module (ice-9 match)
58 #:use-module (ice-9 regex)
59 #:use-module (ice-9 format)
60 #:use-module (web client)
61 #:use-module (web uri)
62 #:use-module ((guix build download)
63 #:select (maybe-expand-mirrors
64 (open-connection-for-uri
65 . guix:open-connection-for-uri)))
66 #:use-module (web request)
67 #:use-module (web response)
68 #:use-module (srfi srfi-1)
69 #:use-module (srfi srfi-6) ;Unicode string ports
70 #:use-module (srfi srfi-9)
71 #:use-module (srfi srfi-11)
72 #:use-module (srfi srfi-26)
73 #:use-module (srfi srfi-34)
74 #:use-module (srfi srfi-35)
75 #:use-module (ice-9 rdelim)
76 #:export (check-description-style
77 check-inputs-should-be-native
78 check-inputs-should-not-be-an-input-at-all
79 check-patch-file-names
80 check-patch-headers
81 check-synopsis-style
82 check-derivation
83 check-home-page
84 check-source
85 check-source-file-name
86 check-source-unstable-tarball
87 check-mirror-url
88 check-github-url
89 check-license
90 check-vulnerabilities
91 check-for-updates
92 check-formatting
93 check-archival
94 check-profile-collisions
95 check-haskell-stackage
96
97 lint-warning
98 lint-warning?
99 lint-warning-package
100 lint-warning-message
101 lint-warning-message-text
102 lint-warning-message-data
103 lint-warning-location
104
105 %local-checkers
106 %network-dependent-checkers
107 %all-checkers
108
109 lint-checker
110 lint-checker?
111 lint-checker-name
112 lint-checker-description
113 lint-checker-check
114 lint-checker-requires-store?))
115
116 \f
117 ;;;
118 ;;; Warnings
119 ;;;
120
121 (define-record-type* <lint-warning>
122 lint-warning make-lint-warning
123 lint-warning?
124 (package lint-warning-package)
125 (message-text lint-warning-message-text)
126 (message-data lint-warning-message-data
127 (default '()))
128 (location lint-warning-location
129 (default #f)))
130
131 (define (lint-warning-message warning)
132 (apply format #f
133 (G_ (lint-warning-message-text warning))
134 (lint-warning-message-data warning)))
135
136 (define (package-file package)
137 (location-file
138 (package-location package)))
139
140 (define* (%make-warning package message-text
141 #:optional (message-data '())
142 #:key field location)
143 (make-lint-warning
144 package
145 message-text
146 message-data
147 (or location
148 (and field (package-field-location package field))
149 (package-location package))))
150
151 (define-syntax make-warning
152 (syntax-rules (G_)
153 ((_ package (G_ message) rest ...)
154 (%make-warning package message rest ...))))
155
156 \f
157 ;;;
158 ;;; Checkers
159 ;;;
160
161 (define-record-type* <lint-checker>
162 lint-checker make-lint-checker
163 lint-checker?
164 ;; TODO: add a 'certainty' field that shows how confident we are in the
165 ;; checker. Then allow users to only run checkers that have a certain
166 ;; 'certainty' level.
167 (name lint-checker-name)
168 (description lint-checker-description)
169 (check lint-checker-check)
170 (requires-store? lint-checker-requires-store?
171 (default #f)))
172
173 (define (properly-starts-sentence? s)
174 (string-match "^[(\"'`[:upper:][:digit:]]" s))
175
176 (define (starts-with-abbreviation? s)
177 "Return #t if S starts with what looks like an abbreviation or acronym."
178 (string-match "^[A-Z][A-Z0-9]+\\>" s))
179
180 (define %quoted-identifier-rx
181 ;; A quoted identifier, like 'this'.
182 (make-regexp "['`][[:graph:]]+'"))
183
184 (define (check-description-style package)
185 ;; Emit a warning if stylistic issues are found in the description of PACKAGE.
186 (define (check-not-empty description)
187 (if (string-null? description)
188 (list
189 (make-warning package
190 (G_ "description should not be empty")
191 #:field 'description))
192 '()))
193
194 (define (check-texinfo-markup description)
195 "Check that DESCRIPTION can be parsed as a Texinfo fragment. If the
196 markup is valid return a plain-text version of DESCRIPTION, otherwise #f."
197 (catch #t
198 (lambda () (texi->plain-text description))
199 (lambda (keys . args)
200 (make-warning package
201 (G_ "Texinfo markup in description is invalid")
202 #:field 'description))))
203
204 (define (check-trademarks description)
205 "Check that DESCRIPTION does not contain '™' or '®' characters. See
206 http://www.gnu.org/prep/standards/html_node/Trademarks.html."
207 (match (string-index description (char-set #\™ #\®))
208 ((and (? number?) index)
209 (list
210 (make-warning package
211 (G_ "description should not contain ~
212 trademark sign '~a' at ~d")
213 (list (string-ref description index) index)
214 #:field 'description)))
215 (else '())))
216
217 (define (check-quotes description)
218 "Check whether DESCRIPTION contains single quotes and suggest @code."
219 (if (regexp-exec %quoted-identifier-rx description)
220 (list
221 (make-warning package
222 ;; TRANSLATORS: '@code' is Texinfo markup and must be kept
223 ;; as is.
224 (G_ "use @code or similar ornament instead of quotes")
225 #:field 'description))
226 '()))
227
228 (define (check-proper-start description)
229 (if (or (string-null? description)
230 (properly-starts-sentence? description)
231 (string-prefix-ci? (package-name package) description))
232 '()
233 (list
234 (make-warning
235 package
236 (G_ "description should start with an upper-case letter or digit")
237 #:field 'description))))
238
239 (define (check-end-of-sentence-space description)
240 "Check that an end-of-sentence period is followed by two spaces."
241 (let ((infractions
242 (reverse (fold-matches
243 "\\. [A-Z]" description '()
244 (lambda (m r)
245 ;; Filter out matches of common abbreviations.
246 (if (find (lambda (s)
247 (string-suffix-ci? s (match:prefix m)))
248 '("i.e" "e.g" "a.k.a" "resp"))
249 r (cons (match:start m) r)))))))
250 (if (null? infractions)
251 '()
252 (list
253 (make-warning package
254 (G_ "sentences in description should be followed ~
255 by two spaces; possible infraction~p at ~{~a~^, ~}")
256 (list (length infractions)
257 infractions)
258 #:field 'description)))))
259
260 (let ((description (package-description package)))
261 (if (string? description)
262 (append
263 (check-not-empty description)
264 (check-quotes description)
265 (check-trademarks description)
266 ;; Use raw description for this because Texinfo rendering
267 ;; automatically fixes end of sentence space.
268 (check-end-of-sentence-space description)
269 (match (check-texinfo-markup description)
270 ((and warning (? lint-warning?)) (list warning))
271 (plain-description
272 (check-proper-start plain-description))))
273 (list
274 (make-warning package
275 (G_ "invalid description: ~s")
276 (list description)
277 #:field 'description)))))
278
279 (define (package-input-intersection inputs-to-check input-names)
280 "Return the intersection between INPUTS-TO-CHECK, the list of input tuples
281 of a package, and INPUT-NAMES, a list of package specifications such as
282 \"glib:bin\"."
283 (match inputs-to-check
284 (((labels packages . outputs) ...)
285 (filter-map (lambda (package output)
286 (and (package? package)
287 (let ((input (string-append
288 (package-name package)
289 (if (> (length output) 0)
290 (string-append ":" (car output))
291 ""))))
292 (and (member input input-names)
293 input))))
294 packages outputs))))
295
296 (define (check-inputs-should-be-native package)
297 ;; Emit a warning if some inputs of PACKAGE are likely to belong to its
298 ;; native inputs.
299 (let ((inputs (append (package-inputs package)
300 (package-propagated-inputs package)))
301 (input-names
302 '("pkg-config"
303 "autoconf"
304 "automake"
305 "bison"
306 "cmake"
307 "dejagnu"
308 "desktop-file-utils"
309 "doxygen"
310 "extra-cmake-modules"
311 "flex"
312 "gettext"
313 "glib:bin"
314 "gobject-introspection"
315 "googletest-source"
316 "groff"
317 "gtk-doc"
318 "help2man"
319 "intltool"
320 "itstool"
321 "libtool"
322 "m4"
323 "qttools"
324 "yasm" "nasm" "fasm"
325 "python-coverage" "python2-coverage"
326 "python-cython" "python2-cython"
327 "python-docutils" "python2-docutils"
328 "python-mock" "python2-mock"
329 "python-nose" "python2-nose"
330 "python-pbr" "python2-pbr"
331 "python-pytest" "python2-pytest"
332 "python-pytest-cov" "python2-pytest-cov"
333 "python-setuptools-scm" "python2-setuptools-scm"
334 "python-sphinx" "python2-sphinx"
335 "scdoc"
336 "swig"
337 "qmake"
338 "qttools"
339 "texinfo"
340 "xorg-server-for-tests"
341 "yelp-tools")))
342 (map (lambda (input)
343 (make-warning
344 package
345 (G_ "'~a' should probably be a native input")
346 (list input)
347 #:field 'inputs))
348 (package-input-intersection inputs input-names))))
349
350 (define (check-inputs-should-not-be-an-input-at-all package)
351 ;; Emit a warning if some inputs of PACKAGE are likely to should not be
352 ;; an input at all.
353 (let ((input-names '("python-setuptools"
354 "python2-setuptools"
355 "python-pip"
356 "python2-pip")))
357 (map (lambda (input)
358 (make-warning
359 package
360 (G_ "'~a' should probably not be an input at all")
361 (list input)
362 #:field 'inputs))
363 (package-input-intersection (package-direct-inputs package)
364 input-names))))
365
366 (define (package-name-regexp package)
367 "Return a regexp that matches PACKAGE's name as a word at the beginning of a
368 line."
369 (make-regexp (string-append "^" (regexp-quote (package-name package))
370 "\\>")
371 regexp/icase))
372
373 (define (check-synopsis-style package)
374 ;; Emit a warning if stylistic issues are found in the synopsis of PACKAGE.
375 (define (check-final-period synopsis)
376 ;; Synopsis should not end with a period, except for some special cases.
377 (if (and (string-suffix? "." synopsis)
378 (not (string-suffix? "etc." synopsis)))
379 (list
380 (make-warning package
381 (G_ "no period allowed at the end of the synopsis")
382 #:field 'synopsis))
383 '()))
384
385 (define check-start-article
386 ;; Skip this check for GNU packages, as suggested by Karl Berry's reply to
387 ;; <http://lists.gnu.org/archive/html/bug-womb/2014-11/msg00000.html>.
388 (if (false-if-exception (gnu-package? package))
389 (const '())
390 (lambda (synopsis)
391 (if (or (string-prefix-ci? "A " synopsis)
392 (string-prefix-ci? "An " synopsis))
393 (list
394 (make-warning package
395 (G_ "no article allowed at the beginning of \
396 the synopsis")
397 #:field 'synopsis))
398 '()))))
399
400 (define (check-synopsis-length synopsis)
401 (if (>= (string-length synopsis) 80)
402 (list
403 (make-warning package
404 (G_ "synopsis should be less than 80 characters long")
405 #:field 'synopsis))
406 '()))
407
408 (define (check-proper-start synopsis)
409 (if (properly-starts-sentence? synopsis)
410 '()
411 (list
412 (make-warning package
413 (G_ "synopsis should start with an upper-case letter or digit")
414 #:field 'synopsis))))
415
416 (define (check-start-with-package-name synopsis)
417 (if (and (regexp-exec (package-name-regexp package) synopsis)
418 (not (starts-with-abbreviation? synopsis)))
419 (list
420 (make-warning package
421 (G_ "synopsis should not start with the package name")
422 #:field 'synopsis))
423 '()))
424
425 (define (check-texinfo-markup synopsis)
426 "Check that SYNOPSIS can be parsed as a Texinfo fragment. If the
427 markup is valid return a plain-text version of SYNOPSIS, otherwise #f."
428 (catch #t
429 (lambda ()
430 (texi->plain-text synopsis)
431 '())
432 (lambda (keys . args)
433 (list
434 (make-warning package
435 (G_ "Texinfo markup in synopsis is invalid")
436 #:field 'synopsis)))))
437
438 (define checks
439 (list check-proper-start
440 check-final-period
441 check-start-article
442 check-start-with-package-name
443 check-synopsis-length
444 check-texinfo-markup))
445
446 (match (package-synopsis package)
447 (""
448 (list
449 (make-warning package
450 (G_ "synopsis should not be empty")
451 #:field 'synopsis)))
452 ((? string? synopsis)
453 (append-map
454 (lambda (proc)
455 (proc synopsis))
456 checks))
457 (invalid
458 (list
459 (make-warning package
460 (G_ "invalid synopsis: ~s")
461 (list invalid)
462 #:field 'synopsis)))))
463
464 (define* (probe-uri uri #:key timeout)
465 "Probe URI, a URI object, and return two values: a symbol denoting the
466 probing status, such as 'http-response' when we managed to get an HTTP
467 response from URI, and additional details, such as the actual HTTP response.
468
469 TIMEOUT is the maximum number of seconds (possibly an inexact number) to wait
470 for connections to complete; when TIMEOUT is #f, wait as long as needed."
471 (define headers
472 '((User-Agent . "GNU Guile")
473 (Accept . "*/*")))
474
475 (let loop ((uri uri)
476 (visited '()))
477 (match (uri-scheme uri)
478 ((or 'http 'https)
479 (catch #t
480 (lambda ()
481 (let ((port (guix:open-connection-for-uri
482 uri #:timeout timeout))
483 (request (build-request uri #:headers headers)))
484 (define response
485 (dynamic-wind
486 (const #f)
487 (lambda ()
488 (write-request request port)
489 (force-output port)
490 (read-response port))
491 (lambda ()
492 (close-port port))))
493
494 (case (response-code response)
495 ((302 ; found (redirection)
496 303 ; see other
497 307 ; temporary redirection
498 308) ; permanent redirection
499 (let ((location (response-location response)))
500 (if (or (not location) (member location visited))
501 (values 'http-response response)
502 (loop location (cons location visited))))) ;follow the redirect
503 ((301) ; moved permanently
504 (let ((location (response-location response)))
505 ;; Return RESPONSE, unless the final response as we follow
506 ;; redirects is not 200.
507 (if location
508 (let-values (((status response2)
509 (loop location (cons location visited))))
510 (case status
511 ((http-response)
512 (values 'http-response
513 (if (= 200 (response-code response2))
514 response
515 response2)))
516 (else
517 (values status response2))))
518 (values 'http-response response)))) ;invalid redirect
519 (else
520 (values 'http-response response)))))
521 (lambda (key . args)
522 (case key
523 ((bad-header bad-header-component)
524 ;; This can happen if the server returns an invalid HTTP header,
525 ;; as is the case with the 'Date' header at sqlite.org.
526 (values 'invalid-http-response #f))
527 ((getaddrinfo-error system-error
528 gnutls-error tls-certificate-error)
529 (values key args))
530 (else
531 (apply throw key args))))))
532 ('ftp
533 (catch #t
534 (lambda ()
535 (let ((conn (ftp-open (uri-host uri) #:timeout timeout)))
536 (define response
537 (dynamic-wind
538 (const #f)
539 (lambda ()
540 (ftp-chdir conn (dirname (uri-path uri)))
541 (ftp-size conn (basename (uri-path uri))))
542 (lambda ()
543 (ftp-close conn))))
544 (values 'ftp-response '(ok))))
545 (lambda (key . args)
546 (case key
547 ((ftp-error)
548 (values 'ftp-response `(error ,@args)))
549 ((getaddrinfo-error system-error gnutls-error)
550 (values key args))
551 (else
552 (apply throw key args))))))
553 (_
554 (values 'unknown-protocol #f)))))
555
556 (define (tls-certificate-error-string args)
557 "Return a string explaining the 'tls-certificate-error' arguments ARGS."
558 (call-with-output-string
559 (lambda (port)
560 (print-exception port #f
561 'tls-certificate-error args))))
562
563 (define (validate-uri uri package field)
564 "Return #t if the given URI can be reached, otherwise return a warning for
565 PACKAGE mentioning the FIELD."
566 (let-values (((status argument)
567 (probe-uri uri #:timeout 3))) ;wait at most 3 seconds
568 (case status
569 ((http-response)
570 (cond ((= 200 (response-code argument))
571 (match (response-content-length argument)
572 ((? number? length)
573 ;; As of July 2016, SourceForge returns 200 (instead of 404)
574 ;; with a small HTML page upon failure. Attempt to detect
575 ;; such malicious behavior.
576 (or (> length 1000)
577 (make-warning package
578 (G_ "URI ~a returned \
579 suspiciously small file (~a bytes)")
580 (list (uri->string uri)
581 length)
582 #:field field)))
583 (_ #t)))
584 ((= 301 (response-code argument))
585 (if (response-location argument)
586 (make-warning package
587 (G_ "permanent redirect from ~a to ~a")
588 (list (uri->string uri)
589 (uri->string
590 (response-location argument)))
591 #:field field)
592 (make-warning package
593 (G_ "invalid permanent redirect \
594 from ~a")
595 (list (uri->string uri))
596 #:field field)))
597 (else
598 (make-warning package
599 (G_ "URI ~a not reachable: ~a (~s)")
600 (list (uri->string uri)
601 (response-code argument)
602 (response-reason-phrase argument))
603 #:field field))))
604 ((ftp-response)
605 (match argument
606 (('ok) #t)
607 (('error port command code message)
608 (make-warning package
609 (G_ "URI ~a not reachable: ~a (~s)")
610 (list (uri->string uri)
611 code (string-trim-both message))
612 #:field field))))
613 ((getaddrinfo-error)
614 (make-warning package
615 (G_ "URI ~a domain not found: ~a")
616 (list (uri->string uri)
617 (gai-strerror (car argument)))
618 #:field field))
619 ((system-error)
620 (make-warning package
621 (G_ "URI ~a unreachable: ~a")
622 (list (uri->string uri)
623 (strerror
624 (system-error-errno
625 (cons status argument))))
626 #:field field))
627 ((tls-certificate-error)
628 (make-warning package
629 (G_ "TLS certificate error: ~a")
630 (list (tls-certificate-error-string argument))
631 #:field field))
632 ((invalid-http-response gnutls-error)
633 ;; Probably a misbehaving server; ignore.
634 #f)
635 ((unknown-protocol) ;nothing we can do
636 #f)
637 (else
638 (error "internal linter error" status)))))
639
640 (define (check-home-page package)
641 "Emit a warning if PACKAGE has an invalid 'home-page' field, or if that
642 'home-page' is not reachable."
643 (let ((uri (and=> (package-home-page package) string->uri)))
644 (cond
645 ((uri? uri)
646 (match (validate-uri uri package 'home-page)
647 ((and (? lint-warning? warning) warning)
648 (list warning))
649 (_ '())))
650 ((not (package-home-page package))
651 (if (or (string-contains (package-name package) "bootstrap")
652 (string=? (package-name package) "ld-wrapper"))
653 '()
654 (list
655 (make-warning package
656 (G_ "invalid value for home page")
657 #:field 'home-page))))
658 (else
659 (list
660 (make-warning package
661 (G_ "invalid home page URL: ~s")
662 (list (package-home-page package))
663 #:field 'home-page))))))
664
665 (define %distro-directory
666 (mlambda ()
667 (dirname (search-path %load-path "gnu.scm"))))
668
669 (define (check-patch-file-names package)
670 "Emit a warning if the patches requires by PACKAGE are badly named or if the
671 patch could not be found."
672 (guard (c ((formatted-message? c) ;raised by 'search-patch'
673 (list (%make-warning package
674 (formatted-message-string c)
675 (formatted-message-arguments c)
676 #:field 'source))))
677 (define patches
678 (match (package-source package)
679 ((? origin? origin) (origin-patches origin))
680 (_ '())))
681
682 (define (starts-with-package-name? file-name)
683 (and=> (string-contains file-name (package-name package))
684 zero?))
685
686 (append
687 (if (every (match-lambda ;patch starts with package name?
688 ((? string? patch)
689 (starts-with-package-name? (basename patch)))
690 ((? origin? patch)
691 (starts-with-package-name? (origin-actual-file-name patch)))
692 (_ #f)) ;must be some other file-like object
693 patches)
694 '()
695 (list
696 (make-warning
697 package
698 (G_ "file names of patches should start with the package name")
699 #:field 'patch-file-names)))
700
701 ;; Check whether we're reaching tar's maximum file name length.
702 (let ((prefix (string-length (%distro-directory)))
703 (margin (string-length "guix-2.0.0rc3-10000-1234567890/"))
704 (max 99))
705 (filter-map (match-lambda
706 ((? string? patch)
707 (if (> (+ margin (if (string-prefix? (%distro-directory)
708 patch)
709 (- (string-length patch) prefix)
710 (string-length patch)))
711 max)
712 (make-warning
713 package
714 (G_ "~a: file name is too long")
715 (list (basename patch))
716 #:field 'patch-file-names)
717 #f))
718 (_ #f))
719 patches)))))
720
721 (define (check-patch-headers package)
722 "Check that PACKAGE's patches start with a comment. Return a list of
723 warnings."
724 (define (blank? str)
725 (string-every char-set:blank str))
726
727 (define (patch-header-warnings patch)
728 (call-with-input-file patch
729 (lambda (port)
730 ;; Read from PORT until a non-blank line is found or EOF is reached.
731 (let loop ()
732 (let ((line (read-line port)))
733 (cond ((eof-object? line)
734 (list (make-warning package
735 (G_ "~a: empty patch")
736 (list (basename patch))
737 #:field 'source)))
738 ((blank? line)
739 (loop))
740 ((or (string-prefix? "--- " line)
741 (string-prefix? "+++ " line))
742 (list (make-warning package
743 (G_ "~a: patch lacks comment and \
744 upstream status")
745 (list (basename patch))
746 #:field 'source)))
747 (else
748 '())))))))
749
750 (guard (c ((formatted-message? c) ;raised by 'search-patch'
751 (list (%make-warning package
752 (formatted-message-string c)
753 (formatted-message-arguments c)
754 #:field 'source))))
755 (let ((patches (if (origin? (package-source package))
756 (origin-patches (package-source package))
757 '())))
758 (append-map (lambda (patch)
759 ;; Dismiss PATCH if it's an origin or similar.
760 (cond ((string? patch)
761 (patch-header-warnings patch))
762 ((local-file? patch)
763 (patch-header-warnings
764 (local-file-absolute-file-name patch)))
765 (else
766 '())))
767 patches))))
768
769 (define (escape-quotes str)
770 "Replace any quote character in STR by an escaped quote character."
771 (list->string
772 (string-fold-right (lambda (chr result)
773 (match chr
774 (#\" (cons* #\\ #\"result))
775 (_ (cons chr result))))
776 '()
777 str)))
778
779 (define official-gnu-packages*
780 (mlambda ()
781 "A memoizing version of 'official-gnu-packages' that returns the empty
782 list when something goes wrong, such as a networking issue."
783 (let ((gnus (false-if-exception (official-gnu-packages))))
784 (or gnus '()))))
785
786 (define (check-gnu-synopsis+description package)
787 "Make sure that, if PACKAGE is a GNU package, it uses the synopsis and
788 descriptions maintained upstream."
789 (match (find (lambda (descriptor)
790 (string=? (gnu-package-name descriptor)
791 (package-name package)))
792 (official-gnu-packages*))
793 (#f ;not a GNU package, so nothing to do
794 '())
795 (descriptor ;a genuine GNU package
796 (append
797 (let ((upstream (gnu-package-doc-summary descriptor))
798 (downstream (package-synopsis package)))
799 (if (and upstream
800 (or (not (string? downstream))
801 (not (string=? upstream downstream))))
802 (list
803 (make-warning package
804 (G_ "proposed synopsis: ~s~%")
805 (list upstream)
806 #:field 'synopsis))
807 '()))
808
809 (let ((upstream (gnu-package-doc-description descriptor))
810 (downstream (package-description package)))
811 (if (and upstream
812 (or (not (string? downstream))
813 (not (string=? (fill-paragraph upstream 100)
814 (fill-paragraph downstream 100)))))
815 (list
816 (make-warning
817 package
818 (G_ "proposed description:~% \"~a\"~%")
819 (list (fill-paragraph (escape-quotes upstream) 77 7))
820 #:field 'description))
821 '()))))))
822
823 (define (origin-uris origin)
824 "Return the list of URIs (strings) for ORIGIN."
825 (match (origin-uri origin)
826 ((? string? uri)
827 (list uri))
828 ((uris ...)
829 uris)))
830
831 (define (check-source package)
832 "Emit a warning if PACKAGE has an invalid 'source' field, or if that
833 'source' is not reachable."
834 (define (warnings-for-uris uris)
835 (let loop ((uris uris)
836 (warnings '()))
837 (match uris
838 (()
839 (reverse warnings))
840 ((uri rest ...)
841 (match (validate-uri uri package 'source)
842 (#t
843 ;; We found a working URL, so stop right away.
844 '())
845 (#f
846 ;; Unsupported URL or other error, skip.
847 (loop rest warnings))
848 ((? lint-warning? warning)
849 (loop rest (cons warning warnings))))))))
850
851 (let ((origin (package-source package)))
852 (if (origin? origin)
853 (cond
854 ((eq? (origin-method origin) url-fetch)
855 (let* ((uris (append-map (cut maybe-expand-mirrors <> %mirrors)
856 (map string->uri (origin-uris origin))))
857 (warnings (warnings-for-uris uris)))
858
859 ;; Just make sure that at least one of the URIs is valid.
860 (if (= (length uris) (length warnings))
861 ;; When everything fails, report all of WARNINGS, otherwise don't
862 ;; report anything.
863 ;;
864 ;; XXX: Ideally we'd still allow warnings to be raised if *some*
865 ;; URIs are unreachable, but distinguish that from the error case
866 ;; where *all* the URIs are unreachable.
867 (cons*
868 (make-warning package
869 (G_ "all the source URIs are unreachable:")
870 #:field 'source)
871 warnings)
872 '())))
873 ((git-reference? (origin-uri origin))
874 (warnings-for-uris
875 (list (string->uri (git-reference-url (origin-uri origin))))))
876 (else
877 '()))
878 '())))
879
880 (define (check-source-file-name package)
881 "Emit a warning if PACKAGE's origin has no meaningful file name."
882 (define (origin-file-name-valid? origin)
883 ;; Return #f if the source file name contains only a version or is #f;
884 ;; indicates that the origin needs a 'file-name' field.
885 (let ((file-name (origin-actual-file-name origin))
886 (version (package-version package)))
887 (and file-name
888 ;; Common in many projects is for the filename to start
889 ;; with a "v" followed by the version,
890 ;; e.g. "v3.2.0.tar.gz".
891 (not (string-match (string-append "^v?" version) file-name)))))
892
893 (let ((origin (package-source package)))
894 (if (or (not (origin? origin)) (origin-file-name-valid? origin))
895 '()
896 (list
897 (make-warning package
898 (G_ "the source file name should contain the package name")
899 #:field 'source)))))
900
901 (define (check-source-unstable-tarball package)
902 "Emit a warning if PACKAGE's source is an autogenerated tarball."
903 (define (check-source-uri uri)
904 (if (and (string=? (uri-host (string->uri uri)) "github.com")
905 (match (split-and-decode-uri-path
906 (uri-path (string->uri uri)))
907 ((_ _ "archive" _ ...) #t)
908 (_ #f)))
909 (make-warning package
910 (G_ "the source URI should not be an autogenerated tarball")
911 #:field 'source)
912 #f))
913
914 (let ((origin (package-source package)))
915 (if (and (origin? origin)
916 (eqv? (origin-method origin) url-fetch))
917 (filter-map check-source-uri
918 (origin-uris origin))
919 '())))
920
921 (define (check-mirror-url package)
922 "Check whether PACKAGE uses source URLs that should be 'mirror://'."
923 (define (check-mirror-uri uri) ;XXX: could be optimized
924 (let loop ((mirrors %mirrors))
925 (match mirrors
926 (()
927 #f)
928 (((mirror-id mirror-urls ...) rest ...)
929 (match (find (cut string-prefix? <> uri) mirror-urls)
930 (#f
931 (loop rest))
932 (prefix
933 (make-warning package
934 (G_ "URL should be \
935 'mirror://~a/~a'")
936 (list mirror-id
937 (string-drop uri (string-length prefix)))
938 #:field 'source)))))))
939
940 (let ((origin (package-source package)))
941 (if (and (origin? origin)
942 (eqv? (origin-method origin) url-fetch))
943 (let ((uris (origin-uris origin)))
944 (filter-map check-mirror-uri uris))
945 '())))
946
947 (define* (check-github-url package #:key (timeout 3))
948 "Check whether PACKAGE uses source URLs that redirect to GitHub."
949 (define (follow-redirect url)
950 (let* ((uri (string->uri url))
951 (port (guix:open-connection-for-uri uri #:timeout timeout))
952 (response (http-head uri #:port port)))
953 (close-port port)
954 (case (response-code response)
955 ((301 302)
956 (uri->string (assoc-ref (response-headers response) 'location)))
957 (else #f))))
958
959 (define (follow-redirects-to-github uri)
960 (cond
961 ((string-prefix? "https://github.com/" uri) uri)
962 ((string-prefix? "http" uri)
963 (and=> (follow-redirect uri) follow-redirects-to-github))
964 ;; Do not attempt to follow redirects on URIs other than http and https
965 ;; (such as mirror, file)
966 (else #f)))
967
968 (let ((origin (package-source package)))
969 (if (and (origin? origin)
970 (eqv? (origin-method origin) url-fetch))
971 (filter-map
972 (lambda (uri)
973 (and=> (follow-redirects-to-github uri)
974 (lambda (github-uri)
975 (if (string=? github-uri uri)
976 #f
977 (make-warning
978 package
979 (G_ "URL should be '~a'")
980 (list github-uri)
981 #:field 'source)))))
982 (origin-uris origin))
983 '())))
984
985 (cond-expand
986 (guile-3
987 ;; Guile 3.0.0 does not export this predicate.
988 (define exception-with-kind-and-args?
989 (exception-predicate &exception-with-kind-and-args)))
990 (else ;Guile 2
991 (define exception-with-kind-and-args?
992 (const #f))))
993
994 (define* (check-derivation package #:key store)
995 "Emit a warning if we fail to compile PACKAGE to a derivation."
996 (define (try store system)
997 (catch #t ;TODO: Remove 'catch' when Guile 2.x is no longer supported.
998 (lambda ()
999 (guard (c ((store-protocol-error? c)
1000 (make-warning package
1001 (G_ "failed to create ~a derivation: ~a")
1002 (list system
1003 (store-protocol-error-message c))))
1004 ((exception-with-kind-and-args? c)
1005 (make-warning package
1006 (G_ "failed to create ~a derivation: ~s")
1007 (list system
1008 (cons (exception-kind c)
1009 (exception-args c)))))
1010 ((message-condition? c)
1011 (make-warning package
1012 (G_ "failed to create ~a derivation: ~a")
1013 (list system
1014 (condition-message c))))
1015 ((formatted-message? c)
1016 (let ((str (apply format #f
1017 (formatted-message-string c)
1018 (formatted-message-arguments c))))
1019 (make-warning package
1020 (G_ "failed to create ~a derivation: ~a")
1021 (list system str)))))
1022 (parameterize ((%graft? #f))
1023 (package-derivation store package system #:graft? #f)
1024
1025 ;; If there's a replacement, make sure we can compute its
1026 ;; derivation.
1027 (match (package-replacement package)
1028 (#f #t)
1029 (replacement
1030 (package-derivation store replacement system
1031 #:graft? #f))))))
1032 (lambda args
1033 (make-warning package
1034 (G_ "failed to create ~a derivation: ~s")
1035 (list system args)))))
1036
1037 (define (check-with-store store)
1038 (filter lint-warning?
1039 (map (cut try store <>) (package-supported-systems package))))
1040
1041 ;; For backwards compatability, don't rely on store being set
1042 (or (and=> store check-with-store)
1043 (with-store store
1044 (check-with-store store))))
1045
1046 (define* (check-profile-collisions package #:key store)
1047 "Check for collisions that would occur when installing PACKAGE as a result
1048 of the propagated inputs it pulls in."
1049 (define (do-check store)
1050 (guard (c ((profile-collision-error? c)
1051 (let ((first (profile-collision-error-entry c))
1052 (second (profile-collision-error-conflict c)))
1053 (define format
1054 (if (string=? (manifest-entry-version first)
1055 (manifest-entry-version second))
1056 manifest-entry-item
1057 (lambda (entry)
1058 (string-append (manifest-entry-name entry) "@"
1059 (manifest-entry-version entry)))))
1060
1061 (list (make-warning package
1062 (G_ "propagated inputs ~a and ~a collide")
1063 (list (format first)
1064 (format second)))))))
1065 ;; Disable grafts to avoid building PACKAGE and its dependencies.
1066 (parameterize ((%graft? #f))
1067 (run-with-store store
1068 (mbegin %store-monad
1069 (check-for-collisions (packages->manifest (list package))
1070 (%current-system))
1071 (return '()))))))
1072
1073 (if store
1074 (do-check store)
1075 (with-store store
1076 (do-check store))))
1077
1078 (define (check-license package)
1079 "Warn about type errors of the 'license' field of PACKAGE."
1080 (match (package-license package)
1081 ((or (? license?)
1082 ((? license?) ...))
1083 '())
1084 (x
1085 (list
1086 (make-warning package (G_ "invalid license field")
1087 #:field 'license)))))
1088
1089 (define (call-with-networking-fail-safe message error-value proc)
1090 "Call PROC catching any network-related errors. Upon a networking error,
1091 display a message including MESSAGE and return ERROR-VALUE."
1092 (guard (c ((http-get-error? c)
1093 (warning (G_ "~a: HTTP GET error for ~a: ~a (~s)~%")
1094 message
1095 (uri->string (http-get-error-uri c))
1096 (http-get-error-code c)
1097 (http-get-error-reason c))
1098 error-value))
1099 (catch #t
1100 proc
1101 (match-lambda*
1102 (('getaddrinfo-error errcode)
1103 (warning (G_ "~a: host lookup failure: ~a~%")
1104 message
1105 (gai-strerror errcode))
1106 error-value)
1107 (('tls-certificate-error args ...)
1108 (warning (G_ "~a: TLS certificate error: ~a")
1109 message
1110 (tls-certificate-error-string args))
1111 error-value)
1112 ((and ('system-error _ ...) args)
1113 (let ((errno (system-error-errno args)))
1114 (if (member errno (list ECONNRESET ECONNABORTED ECONNREFUSED))
1115 (let ((details (call-with-output-string
1116 (lambda (port)
1117 (print-exception port #f (car args)
1118 (cdr args))))))
1119 (warning (G_ "~a: ~a~%") message details)
1120 error-value)
1121 (apply throw args))))
1122 (args
1123 (apply throw args))))))
1124
1125 (define-syntax-rule (with-networking-fail-safe message error-value exp ...)
1126 (call-with-networking-fail-safe message error-value
1127 (lambda () exp ...)))
1128
1129 (define (current-vulnerabilities*)
1130 "Like 'current-vulnerabilities', but return the empty list upon networking
1131 or HTTP errors. This allows network-less operation and makes problems with
1132 the NIST server non-fatal."
1133 (with-networking-fail-safe (G_ "while retrieving CVE vulnerabilities")
1134 '()
1135 (current-vulnerabilities #:timeout 4)))
1136
1137 (define package-vulnerabilities
1138 (let ((lookup (delay (vulnerabilities->lookup-proc
1139 (current-vulnerabilities*)))))
1140 (lambda (package)
1141 "Return a list of vulnerabilities affecting PACKAGE."
1142 ;; First we retrieve the Common Platform Enumeration (CPE) name and
1143 ;; version for PACKAGE, then we can pass them to LOOKUP.
1144 (let ((name (or (assoc-ref (package-properties package)
1145 'cpe-name)
1146 (package-name package)))
1147 (version (or (assoc-ref (package-properties package)
1148 'cpe-version)
1149 (package-version package))))
1150 ((force lookup) name version)))))
1151
1152 (define* (check-vulnerabilities package
1153 #:optional (package-vulnerabilities
1154 package-vulnerabilities))
1155 "Check for known vulnerabilities for PACKAGE. Obtain the list of
1156 vulnerability records for PACKAGE by calling PACKAGE-VULNERABILITIES."
1157 (let ((package (or (package-replacement package) package)))
1158 (match (package-vulnerabilities package)
1159 (()
1160 '())
1161 ((vulnerabilities ...)
1162 (let* ((patched (package-patched-vulnerabilities package))
1163 (known-safe (or (assq-ref (package-properties package)
1164 'lint-hidden-cve)
1165 '()))
1166 (unpatched (remove (lambda (vuln)
1167 (let ((id (vulnerability-id vuln)))
1168 (or (member id patched)
1169 (member id known-safe))))
1170 vulnerabilities)))
1171 (if (null? unpatched)
1172 '()
1173 (list
1174 (make-warning
1175 package
1176 (G_ "probably vulnerable to ~a")
1177 (list (string-join (map vulnerability-id unpatched)
1178 ", "))))))))))
1179
1180 (define (check-for-updates package)
1181 "Check if there is an update available for PACKAGE."
1182 (match (with-networking-fail-safe
1183 (format #f (G_ "while retrieving upstream info for '~a'")
1184 (package-name package))
1185 #f
1186 (package-latest-release* package))
1187 ((? upstream-source? source)
1188 (if (version>? (upstream-source-version source)
1189 (package-version package))
1190 (list
1191 (make-warning package
1192 (G_ "can be upgraded to ~a")
1193 (list (upstream-source-version source))
1194 #:field 'version))
1195 '()))
1196 (#f '()))) ; cannot find newer upstream release
1197
1198
1199 (define (check-archival package)
1200 "Check whether PACKAGE's source code is archived on Software Heritage. If
1201 it's not, and if its source code is a VCS snapshot, then send a \"save\"
1202 request to Software Heritage.
1203
1204 Software Heritage imposes limits on the request rate per client IP address.
1205 This checker prints a notice and stops doing anything once that limit has been
1206 reached."
1207 (define (response->warning url method response)
1208 (if (request-rate-limit-reached? url method)
1209 (list (make-warning package
1210 (G_ "Software Heritage rate limit reached; \
1211 try again later")
1212 #:field 'source))
1213 (list (make-warning package
1214 (G_ "'~a' returned ~a")
1215 (list url (response-code response))
1216 #:field 'source))))
1217
1218 (define skip-key (gensym "skip-archival-check"))
1219
1220 (define (skip-when-limit-reached url method)
1221 (or (not (request-rate-limit-reached? url method))
1222 (throw skip-key #t)))
1223
1224 (parameterize ((%allow-request? skip-when-limit-reached))
1225 (catch #t
1226 (lambda ()
1227 (match (and (origin? (package-source package))
1228 (package-source package))
1229 (#f ;no source
1230 '())
1231 ((= origin-uri (? git-reference? reference))
1232 (define url
1233 (git-reference-url reference))
1234 (define commit
1235 (git-reference-commit reference))
1236
1237 (match (if (commit-id? commit)
1238 (or (lookup-revision commit)
1239 (lookup-origin-revision url commit))
1240 (lookup-origin-revision url commit))
1241 ((? revision? revision)
1242 '())
1243 (#f
1244 ;; Revision is missing from the archive, attempt to save it.
1245 (catch 'swh-error
1246 (lambda ()
1247 (save-origin (git-reference-url reference) "git")
1248 (list (make-warning
1249 package
1250 ;; TRANSLATORS: "Software Heritage" is a proper noun
1251 ;; that must remain untranslated. See
1252 ;; <https://www.softwareheritage.org>.
1253 (G_ "scheduled Software Heritage archival")
1254 #:field 'source)))
1255 (lambda (key url method response . _)
1256 (cond ((= 429 (response-code response))
1257 (list (make-warning
1258 package
1259 (G_ "archival rate limit exceeded; \
1260 try again later")
1261 #:field 'source)))
1262 (else
1263 (response->warning url method response))))))))
1264 ((? origin? origin)
1265 ;; Since "save" origins are not supported for non-VCS source, all
1266 ;; we can do is tell whether a given tarball is available or not.
1267 (if (origin-hash origin) ;XXX: for ungoogled-chromium
1268 (let ((hash (origin-hash origin)))
1269 (match (lookup-content (content-hash-value hash)
1270 (symbol->string
1271 (content-hash-algorithm hash)))
1272 (#f
1273 (list (make-warning package
1274 (G_ "source not archived on Software \
1275 Heritage")
1276 #:field 'source)))
1277 ((? content?)
1278 '())))
1279 '()))))
1280 (match-lambda*
1281 (('swh-error url method response)
1282 (response->warning url method response))
1283 ((key . args)
1284 (if (eq? key skip-key)
1285 '()
1286 (with-networking-fail-safe
1287 (G_ "while connecting to Software Heritage")
1288 '()
1289 (apply throw key args))))))))
1290
1291 (define (check-haskell-stackage package)
1292 "Check whether PACKAGE is a Haskell package ahead of the current
1293 Stackage LTS version."
1294 (match (with-networking-fail-safe
1295 (format #f (G_ "while retrieving upstream info for '~a'")
1296 (package-name package))
1297 #f
1298 (package-latest-release package (list %stackage-updater)))
1299 ((? upstream-source? source)
1300 (if (version>? (package-version package)
1301 (upstream-source-version source))
1302 (list
1303 (make-warning package
1304 (G_ "ahead of Stackage LTS version ~a")
1305 (list (upstream-source-version source))
1306 #:field 'version))
1307 '()))
1308 (#f '())))
1309
1310 \f
1311 ;;;
1312 ;;; Source code formatting.
1313 ;;;
1314
1315 (define (report-tabulations package line line-number)
1316 "Warn about tabulations found in LINE."
1317 (match (string-index line #\tab)
1318 (#f #f)
1319 (index
1320 (make-warning package
1321 (G_ "tabulation on line ~a, column ~a")
1322 (list line-number index)
1323 #:location
1324 (location (package-file package)
1325 line-number
1326 index)))))
1327
1328 (define (report-trailing-white-space package line line-number)
1329 "Warn about trailing white space in LINE."
1330 (and (not (or (string=? line (string-trim-right line))
1331 (string=? line (string #\page))))
1332 (make-warning package
1333 (G_ "trailing white space on line ~a")
1334 (list line-number)
1335 #:location
1336 (location (package-file package)
1337 line-number
1338 0))))
1339
1340 (define (report-long-line package line line-number)
1341 "Emit a warning if LINE is too long."
1342 ;; Note: We don't warn at 80 characters because sometimes hashes and URLs
1343 ;; make it hard to fit within that limit and we want to avoid making too
1344 ;; much noise.
1345 (and (> (string-length line) 90)
1346 (make-warning package
1347 (G_ "line ~a is way too long (~a characters)")
1348 (list line-number (string-length line))
1349 #:location
1350 (location (package-file package)
1351 line-number
1352 0))))
1353
1354 (define %hanging-paren-rx
1355 (make-regexp "^[[:blank:]]*[()]+[[:blank:]]*$"))
1356
1357 (define (report-lone-parentheses package line line-number)
1358 "Emit a warning if LINE contains hanging parentheses."
1359 (and (regexp-exec %hanging-paren-rx line)
1360 (make-warning package
1361 (G_ "parentheses feel lonely, \
1362 move to the previous or next line")
1363 (list line-number)
1364 #:location
1365 (location (package-file package)
1366 line-number
1367 0))))
1368
1369 (define %formatting-reporters
1370 ;; List of procedures that report formatting issues. These are not separate
1371 ;; checkers because they would need to re-read the file.
1372 (list report-tabulations
1373 report-trailing-white-space
1374 report-long-line
1375 report-lone-parentheses))
1376
1377 (define* (report-formatting-issues package file starting-line
1378 #:key (reporters %formatting-reporters))
1379 "Report white-space issues in FILE starting from STARTING-LINE, and report
1380 them for PACKAGE."
1381 (define (sexp-last-line port)
1382 ;; Return the last line of the sexp read from PORT or an estimate thereof.
1383 (define &failure (list 'failure))
1384
1385 (let ((start (ftell port))
1386 (start-line (port-line port))
1387 (sexp (catch 'read-error
1388 (lambda () (read port))
1389 (const &failure))))
1390 (let ((line (port-line port)))
1391 (seek port start SEEK_SET)
1392 (set-port-line! port start-line)
1393 (if (eq? sexp &failure)
1394 (+ start-line 60) ;conservative estimate
1395 line))))
1396
1397 (call-with-input-file file
1398 (lambda (port)
1399 (let loop ((line-number 1)
1400 (last-line #f)
1401 (warnings '()))
1402 (let ((line (read-line port)))
1403 (if (or (eof-object? line)
1404 (and last-line (> line-number last-line)))
1405 warnings
1406 (if (and (= line-number starting-line)
1407 (not last-line))
1408 (loop (+ 1 line-number)
1409 (+ 1 (sexp-last-line port))
1410 warnings)
1411 (loop (+ 1 line-number)
1412 last-line
1413 (append
1414 warnings
1415 (if (< line-number starting-line)
1416 '()
1417 (filter-map (lambda (report)
1418 (report package line line-number))
1419 reporters)))))))))))
1420
1421 (define (check-formatting package)
1422 "Check the formatting of the source code of PACKAGE."
1423 (let ((location (package-location package)))
1424 (if location
1425 ;; Report issues starting from the line before the 'package'
1426 ;; form, which usually contains the 'define' form.
1427 (let ((line (- (location-line location) 1)))
1428 (match (search-path %load-path (location-file location))
1429 ((? string? file)
1430 (report-formatting-issues package file line))
1431 (#f
1432 ;; It could be that LOCATION lists a "true" relative file
1433 ;; name--i.e., not relative to an element of %LOAD-PATH.
1434 (let ((file (location-file location)))
1435 (if (file-exists? file)
1436 (report-formatting-issues package file line)
1437 (list (make-warning package
1438 (G_ "source file not found"))))))))
1439 '())))
1440
1441 \f
1442 ;;;
1443 ;;; List of checkers.
1444 ;;;
1445
1446 (define %local-checkers
1447 (list
1448 (lint-checker
1449 (name 'description)
1450 (description "Validate package descriptions")
1451 (check check-description-style))
1452 (lint-checker
1453 (name 'inputs-should-be-native)
1454 (description "Identify inputs that should be native inputs")
1455 (check check-inputs-should-be-native))
1456 (lint-checker
1457 (name 'inputs-should-not-be-input)
1458 (description "Identify inputs that shouldn't be inputs at all")
1459 (check check-inputs-should-not-be-an-input-at-all))
1460 (lint-checker
1461 (name 'license)
1462 ;; TRANSLATORS: <license> is the name of a data type and must not be
1463 ;; translated.
1464 (description "Make sure the 'license' field is a <license> \
1465 or a list thereof")
1466 (check check-license))
1467 (lint-checker
1468 (name 'mirror-url)
1469 (description "Suggest 'mirror://' URLs")
1470 (check check-mirror-url))
1471 (lint-checker
1472 (name 'source-file-name)
1473 (description "Validate file names of sources")
1474 (check check-source-file-name))
1475 (lint-checker
1476 (name 'source-unstable-tarball)
1477 (description "Check for autogenerated tarballs")
1478 (check check-source-unstable-tarball))
1479 (lint-checker
1480 (name 'derivation)
1481 (description "Report failure to compile a package to a derivation")
1482 (check check-derivation)
1483 (requires-store? #t))
1484 (lint-checker
1485 (name 'profile-collisions)
1486 (description "Report collisions that would occur due to propagated inputs")
1487 (check check-profile-collisions)
1488 (requires-store? #t))
1489 (lint-checker
1490 (name 'patch-file-names)
1491 (description "Validate file names and availability of patches")
1492 (check check-patch-file-names))
1493 (lint-checker
1494 (name 'patch-headers)
1495 (description "Validate patch headers")
1496 (check check-patch-headers))
1497 (lint-checker
1498 (name 'formatting)
1499 (description "Look for formatting issues in the source")
1500 (check check-formatting))))
1501
1502 (define %network-dependent-checkers
1503 (list
1504 (lint-checker
1505 (name 'synopsis)
1506 (description "Validate package synopses")
1507 (check check-synopsis-style))
1508 (lint-checker
1509 (name 'gnu-description)
1510 (description "Validate synopsis & description of GNU packages")
1511 (check check-gnu-synopsis+description))
1512 (lint-checker
1513 (name 'home-page)
1514 (description "Validate home-page URLs")
1515 (check check-home-page))
1516 (lint-checker
1517 (name 'source)
1518 (description "Validate source URLs")
1519 (check check-source))
1520 (lint-checker
1521 (name 'github-url)
1522 (description "Suggest GitHub URLs")
1523 (check check-github-url))
1524 (lint-checker
1525 (name 'cve)
1526 (description "Check the Common Vulnerabilities and Exposures\
1527 (CVE) database")
1528 (check check-vulnerabilities))
1529 (lint-checker
1530 (name 'refresh)
1531 (description "Check the package for new upstream releases")
1532 (check check-for-updates))
1533 (lint-checker
1534 (name 'archival)
1535 (description "Ensure source code archival on Software Heritage")
1536 (check check-archival))
1537 (lint-checker
1538 (name 'haskell-stackage)
1539 (description "Ensure Haskell packages use Stackage LTS versions")
1540 (check check-haskell-stackage))))
1541
1542 (define %all-checkers
1543 (append %local-checkers
1544 %network-dependent-checkers))