lint.scm 51 KB

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