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