upstream: 'download-tarball' gracefully handles missing signatures.
[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)))
c1052667
LC
796 (if (origin? origin)
797 (cond
798 ((eq? (origin-method origin) url-fetch)
799 (let* ((uris (append-map (cut maybe-expand-mirrors <> %mirrors)
800 (map string->uri (origin-uris origin))))
801 (warnings (warnings-for-uris uris)))
802
803 ;; Just make sure that at least one of the URIs is valid.
804 (if (= (length uris) (length warnings))
805 ;; When everything fails, report all of WARNINGS, otherwise don't
806 ;; report anything.
807 ;;
808 ;; XXX: Ideally we'd still allow warnings to be raised if *some*
809 ;; URIs are unreachable, but distinguish that from the error case
810 ;; where *all* the URIs are unreachable.
811 (cons*
812 (make-warning package
813 (G_ "all the source URIs are unreachable:")
814 #:field 'source)
815 warnings)
816 '())))
817 ((git-reference? (origin-uri origin))
818 (warnings-for-uris
819 (list (string->uri (git-reference-url (origin-uri origin))))))
820 (else
821 '())))))
f363c836
CB
822
823(define (check-source-file-name package)
824 "Emit a warning if PACKAGE's origin has no meaningful file name."
825 (define (origin-file-name-valid? origin)
826 ;; Return #f if the source file name contains only a version or is #f;
827 ;; indicates that the origin needs a 'file-name' field.
828 (let ((file-name (origin-actual-file-name origin))
829 (version (package-version package)))
830 (and file-name
831 ;; Common in many projects is for the filename to start
832 ;; with a "v" followed by the version,
833 ;; e.g. "v3.2.0.tar.gz".
834 (not (string-match (string-append "^v?" version) file-name)))))
835
836 (let ((origin (package-source package)))
37c3e0bb 837 (if (or (not (origin? origin)) (origin-file-name-valid? origin))
f363c836
CB
838 '()
839 (list
840 (make-warning package
841 (G_ "the source file name should contain the package name")
842 #:field 'source)))))
843
844(define (check-source-unstable-tarball package)
845 "Emit a warning if PACKAGE's source is an autogenerated tarball."
846 (define (check-source-uri uri)
847 (if (and (string=? (uri-host (string->uri uri)) "github.com")
848 (match (split-and-decode-uri-path
849 (uri-path (string->uri uri)))
850 ((_ _ "archive" _ ...) #t)
851 (_ #f)))
852 (make-warning package
853 (G_ "the source URI should not be an autogenerated tarball")
854 #:field 'source)
855 #f))
856
857 (let ((origin (package-source package)))
858 (if (and (origin? origin)
859 (eqv? (origin-method origin) url-fetch))
860 (filter-map check-source-uri
861 (origin-uris origin))
862 '())))
863
864(define (check-mirror-url package)
865 "Check whether PACKAGE uses source URLs that should be 'mirror://'."
866 (define (check-mirror-uri uri) ;XXX: could be optimized
867 (let loop ((mirrors %mirrors))
868 (match mirrors
869 (()
870 #f)
871 (((mirror-id mirror-urls ...) rest ...)
872 (match (find (cut string-prefix? <> uri) mirror-urls)
873 (#f
874 (loop rest))
875 (prefix
876 (make-warning package
877 (G_ "URL should be \
878'mirror://~a/~a'")
879 (list mirror-id
880 (string-drop uri (string-length prefix)))
881 #:field 'source)))))))
882
883 (let ((origin (package-source package)))
884 (if (and (origin? origin)
885 (eqv? (origin-method origin) url-fetch))
886 (let ((uris (origin-uris origin)))
887 (filter-map check-mirror-uri uris))
888 '())))
889
890(define* (check-github-url package #:key (timeout 3))
891 "Check whether PACKAGE uses source URLs that redirect to GitHub."
892 (define (follow-redirect url)
893 (let* ((uri (string->uri url))
894 (port (guix:open-connection-for-uri uri #:timeout timeout))
895 (response (http-head uri #:port port)))
896 (close-port port)
897 (case (response-code response)
898 ((301 302)
899 (uri->string (assoc-ref (response-headers response) 'location)))
900 (else #f))))
901
902 (define (follow-redirects-to-github uri)
903 (cond
904 ((string-prefix? "https://github.com/" uri) uri)
905 ((string-prefix? "http" uri)
906 (and=> (follow-redirect uri) follow-redirects-to-github))
907 ;; Do not attempt to follow redirects on URIs other than http and https
908 ;; (such as mirror, file)
909 (else #f)))
910
911 (let ((origin (package-source package)))
912 (if (and (origin? origin)
913 (eqv? (origin-method origin) url-fetch))
914 (filter-map
915 (lambda (uri)
916 (and=> (follow-redirects-to-github uri)
917 (lambda (github-uri)
918 (if (string=? github-uri uri)
919 #f
920 (make-warning
921 package
922 (G_ "URL should be '~a'")
923 (list github-uri)
924 #:field 'source)))))
925 (origin-uris origin))
926 '())))
927
fd4c832b
LC
928(cond-expand
929 (guile-3
930 ;; Guile 3.0.0 does not export this predicate.
931 (define exception-with-kind-and-args?
932 (exception-predicate &exception-with-kind-and-args)))
933 (else ;Guile 2
934 (define exception-with-kind-and-args?
935 (const #f))))
936
7826fbc0 937(define* (check-derivation package #:key store)
f363c836 938 "Emit a warning if we fail to compile PACKAGE to a derivation."
7826fbc0 939 (define (try store system)
fd4c832b 940 (catch #t ;TODO: Remove 'catch' when Guile 2.x is no longer supported.
f363c836
CB
941 (lambda ()
942 (guard (c ((store-protocol-error? c)
943 (make-warning package
944 (G_ "failed to create ~a derivation: ~a")
945 (list system
946 (store-protocol-error-message c))))
fd4c832b
LC
947 ((exception-with-kind-and-args? c)
948 (make-warning package
949 (G_ "failed to create ~a derivation: ~s")
950 (list system
951 (cons (exception-kind c)
952 (exception-args c)))))
f363c836
CB
953 ((message-condition? c)
954 (make-warning package
955 (G_ "failed to create ~a derivation: ~a")
956 (list system
957 (condition-message c)))))
7826fbc0
CB
958 (parameterize ((%graft? #f))
959 (package-derivation store package system #:graft? #f)
960
961 ;; If there's a replacement, make sure we can compute its
962 ;; derivation.
963 (match (package-replacement package)
964 (#f #t)
965 (replacement
966 (package-derivation store replacement system
967 #:graft? #f))))))
f363c836
CB
968 (lambda args
969 (make-warning package
970 (G_ "failed to create ~a derivation: ~s")
971 (list system args)))))
972
7826fbc0
CB
973 (define (check-with-store store)
974 (filter lint-warning?
975 (map (cut try store <>) (package-supported-systems package))))
976
977 ;; For backwards compatability, don't rely on store being set
978 (or (and=> store check-with-store)
979 (with-store store
980 (check-with-store store))))
f363c836 981
993023a2
LC
982(define* (check-profile-collisions package #:key store)
983 "Check for collisions that would occur when installing PACKAGE as a result
984of the propagated inputs it pulls in."
985 (define (do-check store)
986 (guard (c ((profile-collision-error? c)
987 (let ((first (profile-collision-error-entry c))
988 (second (profile-collision-error-conflict c)))
989 (define format
990 (if (string=? (manifest-entry-version first)
991 (manifest-entry-version second))
992 manifest-entry-item
993 (lambda (entry)
994 (string-append (manifest-entry-name entry) "@"
995 (manifest-entry-version entry)))))
996
997 (list (make-warning package
998 (G_ "propagated inputs ~a and ~a collide")
999 (list (format first)
1000 (format second)))))))
1001 ;; Disable grafts to avoid building PACKAGE and its dependencies.
1002 (parameterize ((%graft? #f))
1003 (run-with-store store
1004 (mbegin %store-monad
1005 (check-for-collisions (packages->manifest (list package))
1006 (%current-system))
1007 (return '()))))))
1008
1009 (if store
1010 (do-check store)
1011 (with-store store
1012 (do-check store))))
1013
f363c836
CB
1014(define (check-license package)
1015 "Warn about type errors of the 'license' field of PACKAGE."
1016 (match (package-license package)
1017 ((or (? license?)
1018 ((? license?) ...))
1019 '())
1020 (x
1021 (list
1022 (make-warning package (G_ "invalid license field")
1023 #:field 'license)))))
1024
1025(define (call-with-networking-fail-safe message error-value proc)
1026 "Call PROC catching any network-related errors. Upon a networking error,
1027display a message including MESSAGE and return ERROR-VALUE."
1028 (guard (c ((http-get-error? c)
1029 (warning (G_ "~a: HTTP GET error for ~a: ~a (~s)~%")
1030 message
1031 (uri->string (http-get-error-uri c))
1032 (http-get-error-code c)
1033 (http-get-error-reason c))
1034 error-value))
1035 (catch #t
1036 proc
1037 (match-lambda*
1038 (('getaddrinfo-error errcode)
1039 (warning (G_ "~a: host lookup failure: ~a~%")
1040 message
1041 (gai-strerror errcode))
1042 error-value)
1043 (('tls-certificate-error args ...)
1044 (warning (G_ "~a: TLS certificate error: ~a")
1045 message
1046 (tls-certificate-error-string args))
1047 error-value)
900e0fbc
LC
1048 ((and ('system-error _ ...) args)
1049 (let ((errno (system-error-errno args)))
1050 (if (member errno (list ECONNRESET ECONNABORTED ECONNREFUSED))
1051 (let ((details (call-with-output-string
1052 (lambda (port)
1053 (print-exception port #f (car args)
1054 (cdr args))))))
1055 (warning (G_ "~a: ~a~%") message details)
1056 error-value)
1057 (apply throw args))))
f363c836
CB
1058 (args
1059 (apply throw args))))))
1060
1061(define-syntax-rule (with-networking-fail-safe message error-value exp ...)
1062 (call-with-networking-fail-safe message error-value
1063 (lambda () exp ...)))
1064
1065(define (current-vulnerabilities*)
1066 "Like 'current-vulnerabilities', but return the empty list upon networking
1067or HTTP errors. This allows network-less operation and makes problems with
1068the NIST server non-fatal."
1069 (with-networking-fail-safe (G_ "while retrieving CVE vulnerabilities")
1070 '()
1071 (current-vulnerabilities)))
1072
1073(define package-vulnerabilities
1074 (let ((lookup (delay (vulnerabilities->lookup-proc
1075 (current-vulnerabilities*)))))
1076 (lambda (package)
1077 "Return a list of vulnerabilities affecting PACKAGE."
1078 ;; First we retrieve the Common Platform Enumeration (CPE) name and
1079 ;; version for PACKAGE, then we can pass them to LOOKUP.
1080 (let ((name (or (assoc-ref (package-properties package)
1081 'cpe-name)
1082 (package-name package)))
1083 (version (or (assoc-ref (package-properties package)
1084 'cpe-version)
1085 (package-version package))))
1086 ((force lookup) name version)))))
1087
fcb2318e
LC
1088(define* (check-vulnerabilities package
1089 #:optional (package-vulnerabilities
1090 package-vulnerabilities))
1091 "Check for known vulnerabilities for PACKAGE. Obtain the list of
1092vulnerability records for PACKAGE by calling PACKAGE-VULNERABILITIES."
f363c836
CB
1093 (let ((package (or (package-replacement package) package)))
1094 (match (package-vulnerabilities package)
1095 (()
1096 '())
1097 ((vulnerabilities ...)
1098 (let* ((patched (package-patched-vulnerabilities package))
1099 (known-safe (or (assq-ref (package-properties package)
1100 'lint-hidden-cve)
1101 '()))
1102 (unpatched (remove (lambda (vuln)
1103 (let ((id (vulnerability-id vuln)))
1104 (or (member id patched)
1105 (member id known-safe))))
1106 vulnerabilities)))
1107 (if (null? unpatched)
1108 '()
1109 (list
1110 (make-warning
1111 package
1112 (G_ "probably vulnerable to ~a")
1113 (list (string-join (map vulnerability-id unpatched)
1114 ", "))))))))))
1115
1116(define (check-for-updates package)
1117 "Check if there is an update available for PACKAGE."
1118 (match (with-networking-fail-safe
58d5f280
LC
1119 (format #f (G_ "while retrieving upstream info for '~a'")
1120 (package-name package))
f363c836
CB
1121 #f
1122 (package-latest-release* package (force %updaters)))
1123 ((? upstream-source? source)
1124 (if (version>? (upstream-source-version source)
1125 (package-version package))
1126 (list
1127 (make-warning package
1128 (G_ "can be upgraded to ~a")
1129 (list (upstream-source-version source))
1130 #:field 'version))
1131 '()))
1132 (#f '()))) ; cannot find newer upstream release
1133
55549c7b
LC
1134
1135(define (check-archival package)
1136 "Check whether PACKAGE's source code is archived on Software Heritage. If
1137it's not, and if its source code is a VCS snapshot, then send a \"save\"
1138request to Software Heritage.
1139
1140Software Heritage imposes limits on the request rate per client IP address.
1141This checker prints a notice and stops doing anything once that limit has been
1142reached."
1143 (define (response->warning url method response)
1144 (if (request-rate-limit-reached? url method)
1145 (list (make-warning package
1146 (G_ "Software Heritage rate limit reached; \
1147try again later")
1148 #:field 'source))
1149 (list (make-warning package
1150 (G_ "'~a' returned ~a")
1151 (list url (response-code response))
1152 #:field 'source))))
1153
1154 (define skip-key (gensym "skip-archival-check"))
1155
1156 (define (skip-when-limit-reached url method)
1157 (or (not (request-rate-limit-reached? url method))
1158 (throw skip-key #t)))
1159
1160 (parameterize ((%allow-request? skip-when-limit-reached))
1161 (catch #t
1162 (lambda ()
1163 (match (and (origin? (package-source package))
1164 (package-source package))
1165 (#f ;no source
1166 '())
1167 ((= origin-uri (? git-reference? reference))
1168 (define url
1169 (git-reference-url reference))
1170 (define commit
1171 (git-reference-commit reference))
1172
1173 (match (if (commit-id? commit)
1174 (or (lookup-revision commit)
1175 (lookup-origin-revision url commit))
1176 (lookup-origin-revision url commit))
1177 ((? revision? revision)
1178 '())
1179 (#f
1180 ;; Revision is missing from the archive, attempt to save it.
1181 (catch 'swh-error
1182 (lambda ()
1183 (save-origin (git-reference-url reference) "git")
1184 (list (make-warning
1185 package
1186 ;; TRANSLATORS: "Software Heritage" is a proper noun
1187 ;; that must remain untranslated. See
1188 ;; <https://www.softwareheritage.org>.
1189 (G_ "scheduled Software Heritage archival")
1190 #:field 'source)))
1191 (lambda (key url method response . _)
1192 (cond ((= 429 (response-code response))
1193 (list (make-warning
1194 package
1195 (G_ "archival rate limit exceeded; \
1196try again later")
1197 #:field 'source)))
1198 (else
1199 (response->warning url method response))))))))
1200 ((? origin? origin)
1201 ;; Since "save" origins are not supported for non-VCS source, all
1202 ;; we can do is tell whether a given tarball is available or not.
feea1d0e
LC
1203 (if (origin-hash origin) ;XXX: for ungoogled-chromium
1204 (let ((hash (origin-hash origin)))
1205 (match (lookup-content (content-hash-value hash)
1206 (symbol->string
1207 (content-hash-algorithm hash)))
1208 (#f
1209 (list (make-warning package
1210 (G_ "source not archived on Software \
55549c7b 1211Heritage")
feea1d0e
LC
1212 #:field 'source)))
1213 ((? content?)
1214 '())))
55549c7b
LC
1215 '()))))
1216 (match-lambda*
d2fde340 1217 (('swh-error url method response)
55549c7b
LC
1218 (response->warning url method response))
1219 ((key . args)
1220 (if (eq? key skip-key)
1221 '()
c2b2c8e9
LC
1222 (with-networking-fail-safe
1223 (G_ "while connecting to Software Heritage")
1224 '()
1225 (apply throw key args))))))))
55549c7b 1226
f363c836
CB
1227\f
1228;;;
1229;;; Source code formatting.
1230;;;
1231
1232(define (report-tabulations package line line-number)
1233 "Warn about tabulations found in LINE."
1234 (match (string-index line #\tab)
7d09f2e8 1235 (#f #f)
f363c836
CB
1236 (index
1237 (make-warning package
1238 (G_ "tabulation on line ~a, column ~a")
1239 (list line-number index)
1240 #:location
1241 (location (package-file package)
1242 line-number
1243 index)))))
1244
1245(define (report-trailing-white-space package line line-number)
1246 "Warn about trailing white space in LINE."
7d09f2e8
LC
1247 (and (not (or (string=? line (string-trim-right line))
1248 (string=? line (string #\page))))
1249 (make-warning package
1250 (G_ "trailing white space on line ~a")
1251 (list line-number)
1252 #:location
1253 (location (package-file package)
1254 line-number
1255 0))))
f363c836
CB
1256
1257(define (report-long-line package line line-number)
1258 "Emit a warning if LINE is too long."
1259 ;; Note: We don't warn at 80 characters because sometimes hashes and URLs
1260 ;; make it hard to fit within that limit and we want to avoid making too
1261 ;; much noise.
7d09f2e8
LC
1262 (and (> (string-length line) 90)
1263 (make-warning package
1264 (G_ "line ~a is way too long (~a characters)")
1265 (list line-number (string-length line))
1266 #:location
1267 (location (package-file package)
1268 line-number
1269 0))))
f363c836
CB
1270
1271(define %hanging-paren-rx
1272 (make-regexp "^[[:blank:]]*[()]+[[:blank:]]*$"))
1273
1274(define (report-lone-parentheses package line line-number)
1275 "Emit a warning if LINE contains hanging parentheses."
7d09f2e8
LC
1276 (and (regexp-exec %hanging-paren-rx line)
1277 (make-warning package
1278 (G_ "parentheses feel lonely, \
f363c836 1279move to the previous or next line")
7d09f2e8
LC
1280 (list line-number)
1281 #:location
1282 (location (package-file package)
1283 line-number
1284 0))))
f363c836
CB
1285
1286(define %formatting-reporters
1287 ;; List of procedures that report formatting issues. These are not separate
1288 ;; checkers because they would need to re-read the file.
1289 (list report-tabulations
1290 report-trailing-white-space
1291 report-long-line
1292 report-lone-parentheses))
1293
1294(define* (report-formatting-issues package file starting-line
1295 #:key (reporters %formatting-reporters))
1296 "Report white-space issues in FILE starting from STARTING-LINE, and report
1297them for PACKAGE."
1298 (define (sexp-last-line port)
1299 ;; Return the last line of the sexp read from PORT or an estimate thereof.
1300 (define &failure (list 'failure))
1301
1302 (let ((start (ftell port))
1303 (start-line (port-line port))
1304 (sexp (catch 'read-error
1305 (lambda () (read port))
1306 (const &failure))))
1307 (let ((line (port-line port)))
1308 (seek port start SEEK_SET)
1309 (set-port-line! port start-line)
1310 (if (eq? sexp &failure)
1311 (+ start-line 60) ;conservative estimate
1312 line))))
1313
1314 (call-with-input-file file
1315 (lambda (port)
1316 (let loop ((line-number 1)
1317 (last-line #f)
1318 (warnings '()))
1319 (let ((line (read-line port)))
1320 (if (or (eof-object? line)
1321 (and last-line (> line-number last-line)))
1322 warnings
1323 (if (and (= line-number starting-line)
1324 (not last-line))
1325 (loop (+ 1 line-number)
1326 (+ 1 (sexp-last-line port))
1327 warnings)
1328 (loop (+ 1 line-number)
1329 last-line
1330 (append
1331 warnings
1332 (if (< line-number starting-line)
1333 '()
7d09f2e8
LC
1334 (filter-map (lambda (report)
1335 (report package line line-number))
1336 reporters)))))))))))
f363c836
CB
1337
1338(define (check-formatting package)
1339 "Check the formatting of the source code of PACKAGE."
1340 (let ((location (package-location package)))
1341 (if location
1342 (and=> (search-path %load-path (location-file location))
1343 (lambda (file)
1344 ;; Report issues starting from the line before the 'package'
1345 ;; form, which usually contains the 'define' form.
1346 (report-formatting-issues package file
1347 (- (location-line location) 1))))
1348 '())))
1349
1350\f
1351;;;
1352;;; List of checkers.
1353;;;
1354
38f3176a 1355(define %local-checkers
f363c836
CB
1356 (list
1357 (lint-checker
1358 (name 'description)
1359 (description "Validate package descriptions")
1360 (check check-description-style))
f363c836
CB
1361 (lint-checker
1362 (name 'inputs-should-be-native)
1363 (description "Identify inputs that should be native inputs")
1364 (check check-inputs-should-be-native))
1365 (lint-checker
1366 (name 'inputs-should-not-be-input)
1367 (description "Identify inputs that shouldn't be inputs at all")
1368 (check check-inputs-should-not-be-an-input-at-all))
f363c836
CB
1369 (lint-checker
1370 (name 'license)
1371 ;; TRANSLATORS: <license> is the name of a data type and must not be
1372 ;; translated.
1373 (description "Make sure the 'license' field is a <license> \
1374or a list thereof")
1375 (check check-license))
f363c836
CB
1376 (lint-checker
1377 (name 'mirror-url)
1378 (description "Suggest 'mirror://' URLs")
1379 (check check-mirror-url))
f363c836
CB
1380 (lint-checker
1381 (name 'source-file-name)
1382 (description "Validate file names of sources")
1383 (check check-source-file-name))
1384 (lint-checker
1385 (name 'source-unstable-tarball)
1386 (description "Check for autogenerated tarballs")
1387 (check check-source-unstable-tarball))
1388 (lint-checker
d84ad6a2
CB
1389 (name 'derivation)
1390 (description "Report failure to compile a package to a derivation")
1391 (check check-derivation)
1392 (requires-store? #t))
993023a2
LC
1393 (lint-checker
1394 (name 'profile-collisions)
1395 (description "Report collisions that would occur due to propagated inputs")
1396 (check check-profile-collisions)
1397 (requires-store? #t))
38f3176a
CB
1398 (lint-checker
1399 (name 'patch-file-names)
1400 (description "Validate file names and availability of patches")
1401 (check check-patch-file-names))
1402 (lint-checker
1403 (name 'formatting)
1404 (description "Look for formatting issues in the source")
1405 (check check-formatting))))
1406
1407(define %network-dependent-checkers
1408 (list
f363c836
CB
1409 (lint-checker
1410 (name 'synopsis)
1411 (description "Validate package synopses")
1412 (check check-synopsis-style))
38f3176a
CB
1413 (lint-checker
1414 (name 'gnu-description)
1415 (description "Validate synopsis & description of GNU packages")
1416 (check check-gnu-synopsis+description))
1417 (lint-checker
1418 (name 'home-page)
1419 (description "Validate home-page URLs")
1420 (check check-home-page))
1421 (lint-checker
1422 (name 'source)
1423 (description "Validate source URLs")
1424 (check check-source))
1425 (lint-checker
1426 (name 'github-url)
1427 (description "Suggest GitHub URLs")
1428 (check check-github-url))
9efa2c28
LC
1429 (lint-checker
1430 (name 'cve)
1431 (description "Check the Common Vulnerabilities and Exposures\
1432 (CVE) database")
1433 (check check-vulnerabilities))
f363c836
CB
1434 (lint-checker
1435 (name 'refresh)
1436 (description "Check the package for new upstream releases")
55549c7b
LC
1437 (check check-for-updates))
1438 (lint-checker
1439 (name 'archival)
1440 (description "Ensure source code archival on Software Heritage")
1441 (check check-archival))))
38f3176a
CB
1442
1443(define %all-checkers
1444 (append %local-checkers
1445 %network-dependent-checkers))