gnu-maintenance.scm 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2012, 2013 Nikita Karetnikov <nikita@karetnikov.org>
  4. ;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (guix gnu-maintenance)
  21. #:use-module (web uri)
  22. #:use-module (web client)
  23. #:use-module (web response)
  24. #:use-module (sxml simple)
  25. #:use-module (ice-9 regex)
  26. #:use-module (ice-9 match)
  27. #:use-module (srfi srfi-1)
  28. #:use-module (srfi srfi-11)
  29. #:use-module (srfi srfi-26)
  30. #:use-module (rnrs io ports)
  31. #:use-module (system foreign)
  32. #:use-module (guix http-client)
  33. #:use-module (guix ftp-client)
  34. #:use-module (guix utils)
  35. #:use-module (guix memoization)
  36. #:use-module (guix records)
  37. #:use-module (guix upstream)
  38. #:use-module (guix packages)
  39. #:use-module (zlib)
  40. #:export (gnu-package-name
  41. gnu-package-mundane-name
  42. gnu-package-copyright-holder
  43. gnu-package-savannah
  44. gnu-package-fsd
  45. gnu-package-language
  46. gnu-package-logo
  47. gnu-package-doc-category
  48. gnu-package-doc-summary
  49. gnu-package-doc-description
  50. gnu-package-doc-urls
  51. gnu-package-download-url
  52. official-gnu-packages
  53. find-package
  54. gnu-package?
  55. release-file?
  56. releases
  57. latest-release
  58. gnu-release-archive-types
  59. gnu-package-name->name+version
  60. %gnu-updater
  61. %gnu-ftp-updater
  62. %savannah-updater
  63. %xorg-updater
  64. %kernel.org-updater))
  65. ;;; Commentary:
  66. ;;;
  67. ;;; Code for dealing with the maintenance of GNU packages, such as
  68. ;;; auto-updates.
  69. ;;;
  70. ;;; Code:
  71. ;;;
  72. ;;; List of GNU packages.
  73. ;;;
  74. (define %gnumaint-base-url
  75. "https://web.cvs.savannah.gnu.org/viewvc/*checkout*/www/www/prep/gnumaint/")
  76. (define %package-list-url
  77. (string->uri
  78. (string-append %gnumaint-base-url "rec/gnupackages.rec")))
  79. (define %package-description-url
  80. ;; This file contains package descriptions in recutils format.
  81. ;; See <https://lists.gnu.org/archive/html/guix-devel/2013-10/msg00071.html>
  82. ;; and <https://lists.gnu.org/archive/html/guix-devel/2018-06/msg00362.html>.
  83. (string->uri
  84. (string-append %gnumaint-base-url "rec/pkgblurbs.rec")))
  85. (define-record-type* <gnu-package-descriptor>
  86. gnu-package-descriptor
  87. make-gnu-package-descriptor
  88. gnu-package-descriptor?
  89. (name gnu-package-name)
  90. (mundane-name gnu-package-mundane-name)
  91. (copyright-holder gnu-package-copyright-holder)
  92. (savannah gnu-package-savannah)
  93. (fsd gnu-package-fsd)
  94. (language gnu-package-language) ; list of strings
  95. (logo gnu-package-logo)
  96. (doc-category gnu-package-doc-category)
  97. (doc-summary gnu-package-doc-summary)
  98. (doc-description gnu-package-doc-description) ; taken from 'pkgdescr.txt'
  99. (doc-urls gnu-package-doc-urls) ; list of strings
  100. (download-url gnu-package-download-url))
  101. (define* (official-gnu-packages
  102. #:optional (fetch http-fetch/cached))
  103. "Return a list of records, which are GNU packages. Use FETCH,
  104. to fetch the list of GNU packages over HTTP."
  105. (define (read-records port)
  106. ;; Return a list of alists. Each alist contains fields of a GNU
  107. ;; package.
  108. (let loop ((alist (recutils->alist port))
  109. (result '()))
  110. (if (null? alist)
  111. (reverse result)
  112. (loop (recutils->alist port)
  113. ;; Ignore things like "%rec" (info "(recutils) Record
  114. ;; Descriptors").
  115. (if (assoc-ref alist "package")
  116. (cons alist result)
  117. result)))))
  118. (define official-description
  119. (let ((db (read-records (fetch %package-description-url #:text? #t))))
  120. (lambda (name)
  121. ;; Return the description found upstream for package NAME, or #f.
  122. (and=> (find (lambda (alist)
  123. (equal? name (assoc-ref alist "package")))
  124. db)
  125. (lambda (record)
  126. (let ((field (assoc-ref record "blurb")))
  127. ;; The upstream description file uses "redirect PACKAGE" as
  128. ;; a blurb in cases where the description of the two
  129. ;; packages should be considered the same (e.g., GTK+ has
  130. ;; "redirect gnome".) This is usually not acceptable for
  131. ;; us because we prefer to have distinct descriptions in
  132. ;; such cases. Thus, ignore the 'blurb' field when that
  133. ;; happens.
  134. (and field
  135. (not (string-prefix? "redirect " field))
  136. field)))))))
  137. (map (lambda (alist)
  138. (let ((name (assoc-ref alist "package")))
  139. (alist->record `(("description" . ,(official-description name))
  140. ,@alist)
  141. make-gnu-package-descriptor
  142. (list "package" "mundane_name" "copyright_holder"
  143. "savannah" "fsd" "language" "logo"
  144. "doc_category" "doc_summary" "description"
  145. "doc_url"
  146. "download_url")
  147. '("doc_url" "language"))))
  148. (let* ((port (fetch %package-list-url #:text? #t))
  149. (lst (read-records port)))
  150. (close-port port)
  151. lst)))
  152. (define (find-package name)
  153. "Find GNU package called NAME and return it. Return #f if it was not
  154. found."
  155. (find (lambda (package)
  156. (string=? name (gnu-package-name package)))
  157. (official-gnu-packages)))
  158. (define gnu-package?
  159. (let ((official-gnu-packages (memoize official-gnu-packages)))
  160. (mlambdaq (package)
  161. "Return true if PACKAGE is a GNU package. This procedure may access the
  162. network to check in GNU's database."
  163. (define (mirror-type url)
  164. (let ((uri (string->uri url)))
  165. (and (eq? (uri-scheme uri) 'mirror)
  166. (cond
  167. ((member (uri-host uri)
  168. '("gnu" "gnupg" "gcc" "gnome"))
  169. ;; Definitely GNU.
  170. 'gnu)
  171. ((equal? (uri-host uri) "cran")
  172. ;; Possibly GNU: mirror://cran could be either GNU R itself
  173. ;; or a non-GNU package.
  174. #f)
  175. (else
  176. ;; Definitely non-GNU.
  177. 'non-gnu)))))
  178. (define (gnu-home-page? package)
  179. (letrec-syntax ((>> (syntax-rules ()
  180. ((_ value proc)
  181. (and=> value proc))
  182. ((_ value proc rest ...)
  183. (and=> value
  184. (lambda (next)
  185. (>> (proc next) rest ...)))))))
  186. (>> package package-home-page
  187. string->uri uri-host
  188. (lambda (host)
  189. (member host '("www.gnu.org" "gnu.org"))))))
  190. (or (gnu-home-page? package)
  191. (match (package-source package)
  192. ((? origin? origin)
  193. (let ((url (origin-uri origin))
  194. (name (package-upstream-name package)))
  195. (case (and (string? url) (mirror-type url))
  196. ((gnu) #t)
  197. ((non-gnu) #f)
  198. (else
  199. (and (member name (map gnu-package-name (official-gnu-packages)))
  200. #t)))))
  201. (_ #f))))))
  202. ;;;
  203. ;;; Latest FTP release.
  204. ;;;
  205. (define (ftp-server/directory package)
  206. "Return the FTP server and directory where PACKAGE's tarball are stored."
  207. (let ((name (package-upstream-name package)))
  208. (values (or (assoc-ref (package-properties package) 'ftp-server)
  209. "ftp.gnu.org")
  210. (or (assoc-ref (package-properties package) 'ftp-directory)
  211. (string-append "/gnu/" name)))))
  212. (define %tarball-rx
  213. ;; The .zip extensions is notably used for freefont-ttf.
  214. ;; The "-src" pattern is for "TeXmacs-1.0.7.9-src.tar.gz".
  215. ;; The "-gnu[0-9]" pattern is for "icecat-38.4.0-gnu1.tar.bz2".
  216. (make-regexp "^([^.]+)-([0-9]|[^-])+(-(src|gnu[0-9]))?\\.(tar\\.|zip$)"))
  217. (define %alpha-tarball-rx
  218. (make-regexp "^.*-.*[0-9](-|~)?(alpha|beta|rc|RC|cvs|svn|git)-?[0-9\\.]*\\.tar\\."))
  219. (define (release-file? project file)
  220. "Return #f if FILE is not a release tarball of PROJECT, otherwise return
  221. true."
  222. (and (not (member (file-extension file) '("sig" "sign" "asc")))
  223. (and=> (regexp-exec %tarball-rx file)
  224. (lambda (match)
  225. ;; Filter out unrelated files, like `guile-www-1.1.1'.
  226. ;; Case-insensitive for things like "TeXmacs" vs. "texmacs".
  227. ;; The "-src" suffix is for "freefont-src-20120503.tar.gz".
  228. (and=> (match:substring match 1)
  229. (lambda (name)
  230. (or (string-ci=? name project)
  231. (string-ci=? name
  232. (string-append project
  233. "-src")))))))
  234. (not (regexp-exec %alpha-tarball-rx file))
  235. (let ((s (tarball-sans-extension file)))
  236. (regexp-exec %package-name-rx s))))
  237. (define (tarball->version tarball)
  238. "Return the version TARBALL corresponds to. TARBALL is a file name like
  239. \"coreutils-8.23.tar.xz\"."
  240. (let-values (((name version)
  241. (gnu-package-name->name+version
  242. (tarball-sans-extension tarball))))
  243. version))
  244. (define* (releases project
  245. #:key
  246. (server "ftp.gnu.org")
  247. (directory (string-append "/gnu/" project)))
  248. "Return the list of <upstream-release> of PROJECT as a list of release
  249. name/directory pairs."
  250. ;; TODO: Parse something like fencepost.gnu.org:/gd/gnuorg/packages-ftp.
  251. (define conn (ftp-open server))
  252. (let loop ((directories (list directory))
  253. (result '()))
  254. (match directories
  255. (()
  256. (ftp-close conn)
  257. (coalesce-sources result))
  258. ((directory rest ...)
  259. (let* ((files (ftp-list conn directory))
  260. (subdirs (filter-map (match-lambda
  261. ((name 'directory . _) name)
  262. (_ #f))
  263. files)))
  264. (define (file->url file)
  265. (string-append "ftp://" server directory "/" file))
  266. (define (file->source file)
  267. (let ((url (file->url file)))
  268. (upstream-source
  269. (package project)
  270. (version (tarball->version file))
  271. (urls (list url))
  272. (signature-urls (list (string-append url ".sig"))))))
  273. (loop (append (map (cut string-append directory "/" <>)
  274. subdirs)
  275. rest)
  276. (append
  277. ;; Filter out signatures, deltas, and files which
  278. ;; are potentially not releases of PROJECT--e.g.,
  279. ;; in /gnu/guile, filter out guile-oops and
  280. ;; guile-www; in mit-scheme, filter out binaries.
  281. (filter-map (match-lambda
  282. ((file 'file . _)
  283. (and (release-file? project file)
  284. (file->source file)))
  285. (_ #f))
  286. files)
  287. result)))))))
  288. (define* (latest-ftp-release project
  289. #:key
  290. (server "ftp.gnu.org")
  291. (directory (string-append "/gnu/" project))
  292. (keep-file? (const #t))
  293. (file->signature (cut string-append <> ".sig"))
  294. (ftp-open ftp-open) (ftp-close ftp-close))
  295. "Return an <upstream-source> for the latest release of PROJECT on SERVER
  296. under DIRECTORY, or #f. Use FTP-OPEN and FTP-CLOSE to open (resp. close) FTP
  297. connections; this can be useful to reuse connections.
  298. KEEP-FILE? is a predicate to decide whether to enter a directory and to
  299. consider a given file (source tarball) as a valid candidate based on its name.
  300. FILE->SIGNATURE must be a procedure; it is passed a source file URL and must
  301. return the corresponding signature URL, or #f it signatures are unavailable."
  302. (define (latest a b)
  303. (if (version>? a b) a b))
  304. (define (latest-release a b)
  305. (if (version>? (upstream-source-version a) (upstream-source-version b))
  306. a b))
  307. (define patch-directory-name?
  308. ;; Return #t for patch directory names such as 'bash-4.2-patches'.
  309. (cut string-suffix? "patches" <>))
  310. (define conn (ftp-open server))
  311. (define (file->url directory file)
  312. (string-append "ftp://" server directory "/" file))
  313. (define (file->source directory file)
  314. (let ((url (file->url directory file)))
  315. (upstream-source
  316. (package project)
  317. (version (tarball->version file))
  318. (urls (list url))
  319. (signature-urls (match (file->signature url)
  320. (#f #f)
  321. (sig (list sig)))))))
  322. (let loop ((directory directory)
  323. (result #f))
  324. (let* ((entries (catch 'ftp-error
  325. (lambda _ (ftp-list conn directory))
  326. (const '())))
  327. ;; Filter out things like /gnupg/patches. Filter out "w32"
  328. ;; directories as found on ftp.gnutls.org.
  329. (subdirs (filter-map (match-lambda
  330. (((? patch-directory-name? dir)
  331. 'directory . _)
  332. #f)
  333. (("w32" 'directory . _)
  334. #f)
  335. (("unstable" 'directory . _)
  336. ;; As seen at ftp.gnupg.org/gcrypt/pinentry.
  337. #f)
  338. ((directory 'directory . _)
  339. directory)
  340. (_ #f))
  341. entries))
  342. ;; Whether or not SUBDIRS is empty, compute the latest releases
  343. ;; for the current directory. This is necessary for packages
  344. ;; such as 'sharutils' that have a sub-directory that contains
  345. ;; only an older release.
  346. (releases (filter-map (match-lambda
  347. ((file 'file . _)
  348. (and (release-file? project file)
  349. (keep-file? file)
  350. (file->source directory file)))
  351. (_ #f))
  352. entries)))
  353. ;; Assume that SUBDIRS correspond to versions, and jump into the
  354. ;; one with the highest version number.
  355. (let* ((release (reduce latest-release #f
  356. (coalesce-sources releases)))
  357. (result (if (and result release)
  358. (latest-release release result)
  359. (or release result)))
  360. (target (reduce latest #f subdirs)))
  361. (if target
  362. (loop (string-append directory "/" target)
  363. result)
  364. (begin
  365. (ftp-close conn)
  366. result))))))
  367. (define* (latest-release package
  368. #:key
  369. (server "ftp.gnu.org")
  370. (directory (string-append "/gnu/" package)))
  371. "Return the <upstream-source> for the latest version of PACKAGE or #f.
  372. PACKAGE must be the canonical name of a GNU package."
  373. (latest-ftp-release package
  374. #:server server
  375. #:directory directory))
  376. (define-syntax-rule (false-if-ftp-error exp)
  377. "Return #f if an FTP error is raise while evaluating EXP; return the result
  378. of EXP otherwise."
  379. (catch 'ftp-error
  380. (lambda ()
  381. exp)
  382. (lambda (key port . rest)
  383. (if (ftp-connection? port)
  384. (ftp-close port)
  385. (close-port port))
  386. #f)))
  387. (define (latest-release* package)
  388. "Like 'latest-release', but (1) take a <package> object, and (2) ignore FTP
  389. errors that might occur when PACKAGE is not actually a GNU package, or not
  390. hosted on ftp.gnu.org, or not under that name (this is the case for
  391. \"emacs-auctex\", for instance.)"
  392. (let-values (((server directory)
  393. (ftp-server/directory package)))
  394. (false-if-ftp-error (latest-release (package-upstream-name package)
  395. #:server server
  396. #:directory directory))))
  397. ;;;
  398. ;;; Latest HTTP release.
  399. ;;;
  400. (define (html->sxml port)
  401. "Read HTML from PORT and return the corresponding SXML tree."
  402. (let ((str (get-string-all port)))
  403. (catch #t
  404. (lambda ()
  405. ;; XXX: This is the poor developer's HTML-to-XML converter. It's good
  406. ;; enough for directory listings at <https://kernel.org/pub> but if
  407. ;; needed we could resort to (htmlprag) from Guile-Lib.
  408. (call-with-input-string (string-replace-substring str "<hr>" "<hr />")
  409. xml->sxml))
  410. (const '(html))))) ;parse error
  411. (define (html-links sxml)
  412. "Return the list of links found in SXML, the SXML tree of an HTML page."
  413. (let loop ((sxml sxml)
  414. (links '()))
  415. (match sxml
  416. (('a ('@ attributes ...) body ...)
  417. (match (assq 'href attributes)
  418. (#f (fold loop links body))
  419. (('href url) (fold loop (cons url links) body))))
  420. ((tag ('@ _ ...) body ...)
  421. (fold loop links body))
  422. ((tag body ...)
  423. (fold loop links body))
  424. (_
  425. links))))
  426. (define* (latest-html-release package
  427. #:key
  428. (base-url "https://kernel.org/pub")
  429. (directory (string-append "/" package))
  430. (file->signature (cut string-append <> ".sig")))
  431. "Return an <upstream-source> for the latest release of PACKAGE (a string) on
  432. SERVER under DIRECTORY, or #f. BASE-URL should be the URL of an HTML page,
  433. typically a directory listing as found on 'https://kernel.org/pub'.
  434. FILE->SIGNATURE must be a procedure; it is passed a source file URL and must
  435. return the corresponding signature URL, or #f it signatures are unavailable."
  436. (let* ((uri (string->uri (string-append base-url directory "/")))
  437. (port (http-fetch/cached uri #:ttl 3600))
  438. (sxml (html->sxml port)))
  439. (define (url->release url)
  440. (and (string=? url (basename url)) ;relative reference?
  441. (release-file? package url)
  442. (let-values (((name version)
  443. (package-name->name+version
  444. (tarball-sans-extension url)
  445. #\-)))
  446. (upstream-source
  447. (package name)
  448. (version version)
  449. (urls (list (string-append base-url directory "/" url)))
  450. (signature-urls
  451. (list (file->signature
  452. (string-append base-url directory "/" url))))))))
  453. (define candidates
  454. (filter-map url->release (html-links sxml)))
  455. (close-port port)
  456. (match candidates
  457. (() #f)
  458. ((first . _)
  459. ;; Select the most recent release and return it.
  460. (reduce (lambda (r1 r2)
  461. (if (version>? (upstream-source-version r1)
  462. (upstream-source-version r2))
  463. r1 r2))
  464. first
  465. (coalesce-sources candidates))))))
  466. ;;;
  467. ;;; Updaters.
  468. ;;;
  469. (define %gnu-file-list-uri
  470. ;; URI of the file list for ftp.gnu.org.
  471. (string->uri "https://ftp.gnu.org/find.txt.gz"))
  472. (define ftp.gnu.org-files
  473. (mlambda ()
  474. "Return the list of files available at ftp.gnu.org."
  475. ;; XXX: Memoize the whole procedure to work around the fact that
  476. ;; 'http-fetch/cached' caches the gzipped version.
  477. (define (trim-leading-components str)
  478. ;; Trim the leading ".", if any, in "./gnu/foo".
  479. (string-trim str (char-set #\.)))
  480. (define (string->lines str)
  481. (string-tokenize str (char-set-complement (char-set #\newline))))
  482. ;; Since https://ftp.gnu.org honors 'If-Modified-Since', the hard-coded
  483. ;; TTL can be relatively short.
  484. (let ((port (http-fetch/cached %gnu-file-list-uri #:ttl (* 15 60))))
  485. (map trim-leading-components
  486. (call-with-gzip-input-port port
  487. (compose string->lines get-string-all))))))
  488. (define (latest-gnu-release package)
  489. "Return the latest release of PACKAGE, a GNU package available via
  490. ftp.gnu.org.
  491. This method does not rely on FTP access at all; instead, it browses the file
  492. list available from %GNU-FILE-LIST-URI over HTTP(S)."
  493. (let-values (((server directory)
  494. (ftp-server/directory package))
  495. ((name)
  496. (package-upstream-name package)))
  497. (let* ((files (ftp.gnu.org-files))
  498. (relevant (filter (lambda (file)
  499. (and (string-prefix? "/gnu" file)
  500. (string-contains file directory)
  501. (release-file? name (basename file))))
  502. files)))
  503. (match (sort relevant (lambda (file1 file2)
  504. (version>? (tarball-sans-extension
  505. (basename file1))
  506. (tarball-sans-extension
  507. (basename file2)))))
  508. ((and tarballs (reference _ ...))
  509. (let* ((version (tarball->version reference))
  510. (tarballs (filter (lambda (file)
  511. (string=? (tarball-sans-extension
  512. (basename file))
  513. (tarball-sans-extension
  514. (basename reference))))
  515. tarballs)))
  516. (upstream-source
  517. (package name)
  518. (version version)
  519. (urls (map (lambda (file)
  520. (string-append "mirror://gnu/"
  521. (string-drop file
  522. (string-length "/gnu/"))))
  523. tarballs))
  524. (signature-urls (map (cut string-append <> ".sig") urls)))))
  525. (()
  526. #f)))))
  527. (define %package-name-rx
  528. ;; Regexp for a package name, e.g., "foo-X.Y". Since TeXmacs uses
  529. ;; "TeXmacs-X.Y-src", the `-src' suffix is allowed.
  530. (make-regexp "^(.*)-(([0-9]|\\.)+)(-src)?"))
  531. (define (gnu-package-name->name+version name+version)
  532. "Return the package name and version number extracted from NAME+VERSION."
  533. (let ((match (regexp-exec %package-name-rx name+version)))
  534. (if (not match)
  535. (values name+version #f)
  536. (values (match:substring match 1) (match:substring match 2)))))
  537. (define gnome-package?
  538. (url-prefix-predicate "mirror://gnome/"))
  539. (define (pure-gnu-package? package)
  540. "Return true if PACKAGE is a non-Emacs and non-GNOME GNU package. This
  541. excludes AucTeX, for instance, whose releases are now uploaded to
  542. elpa.gnu.org, and all the GNOME packages; EMMS is included though, because its
  543. releases are on gnu.org."
  544. (and (or (not (string-prefix? "emacs-" (package-name package)))
  545. (gnu-hosted? package))
  546. (not (gnome-package? package))
  547. (gnu-package? package)))
  548. (define gnu-hosted?
  549. (url-prefix-predicate "mirror://gnu/"))
  550. (define (url-prefix-rewrite old new)
  551. "Return a one-argument procedure that rewrites URL prefix OLD to NEW."
  552. (lambda (url)
  553. (if (string-prefix? old url)
  554. (string-append new (string-drop url (string-length old)))
  555. url)))
  556. (define (adjusted-upstream-source source rewrite-url)
  557. "Rewrite URLs in SOURCE by apply REWRITE-URL to each of them."
  558. (upstream-source
  559. (inherit source)
  560. (urls (map rewrite-url (upstream-source-urls source)))
  561. (signature-urls (and=> (upstream-source-signature-urls source)
  562. (lambda (urls)
  563. (map rewrite-url urls))))))
  564. (define savannah-package?
  565. (url-prefix-predicate "mirror://savannah/"))
  566. (define %savannah-base
  567. ;; One of the Savannah mirrors listed at
  568. ;; <http://download0.savannah.gnu.org/mirmon/savannah/> that serves valid
  569. ;; HTML (unlike <https://download.savannah.nongnu.org/releases>.)
  570. "https://nongnu.freemirror.org/nongnu")
  571. (define (latest-savannah-release package)
  572. "Return the latest release of PACKAGE."
  573. (let* ((uri (string->uri
  574. (match (origin-uri (package-source package))
  575. ((? string? uri) uri)
  576. ((uri mirrors ...) uri))))
  577. (package (package-upstream-name package))
  578. (directory (dirname (uri-path uri)))
  579. (rewrite (url-prefix-rewrite %savannah-base
  580. "mirror://savannah")))
  581. ;; Note: We use the default 'file->signature', which adds ".sig", but not
  582. ;; all projects on Savannah follow that convention: some use ".asc" and
  583. ;; perhaps some lack signatures altogether.
  584. (and=> (latest-html-release package
  585. #:base-url %savannah-base
  586. #:directory directory)
  587. (cut adjusted-upstream-source <> rewrite))))
  588. (define (latest-xorg-release package)
  589. "Return the latest release of PACKAGE."
  590. (let ((uri (string->uri (origin-uri (package-source package)))))
  591. (false-if-ftp-error
  592. (latest-ftp-release
  593. (package-name package)
  594. #:server "ftp.freedesktop.org"
  595. #:directory
  596. (string-append "/pub/xorg/" (dirname (uri-path uri)))))))
  597. (define (latest-kernel.org-release package)
  598. "Return the latest release of PACKAGE, the name of a kernel.org package."
  599. (define %kernel.org-base
  600. ;; This URL and sub-directories thereof are nginx-generated directory
  601. ;; listings suitable for 'latest-html-release'.
  602. "https://mirrors.edge.kernel.org/pub")
  603. (define (file->signature file)
  604. (string-append (file-sans-extension file) ".sign"))
  605. (let* ((uri (string->uri
  606. (match (origin-uri (package-source package))
  607. ((? string? uri) uri)
  608. ((uri mirrors ...) uri))))
  609. (package (package-upstream-name package))
  610. (directory (dirname (uri-path uri)))
  611. (rewrite (url-prefix-rewrite %kernel.org-base
  612. "mirror://kernel.org")))
  613. (and=> (latest-html-release package
  614. #:base-url %kernel.org-base
  615. #:directory directory
  616. #:file->signature file->signature)
  617. (cut adjusted-upstream-source <> rewrite))))
  618. (define %gnu-updater
  619. ;; This is for everything at ftp.gnu.org.
  620. (upstream-updater
  621. (name 'gnu)
  622. (description "Updater for GNU packages")
  623. (pred gnu-hosted?)
  624. (latest latest-gnu-release)))
  625. (define %gnu-ftp-updater
  626. ;; This is for GNU packages taken from alternate locations, such as
  627. ;; alpha.gnu.org, ftp.gnupg.org, etc. It is obsolescent.
  628. (upstream-updater
  629. (name 'gnu-ftp)
  630. (description "Updater for GNU packages only available via FTP")
  631. (pred (lambda (package)
  632. (and (not (gnu-hosted? package))
  633. (pure-gnu-package? package))))
  634. (latest latest-release*)))
  635. (define %savannah-updater
  636. (upstream-updater
  637. (name 'savannah)
  638. (description "Updater for packages hosted on savannah.gnu.org")
  639. (pred (url-prefix-predicate "mirror://savannah/"))
  640. (latest latest-savannah-release)))
  641. (define %xorg-updater
  642. (upstream-updater
  643. (name 'xorg)
  644. (description "Updater for X.org packages")
  645. (pred (url-prefix-predicate "mirror://xorg/"))
  646. (latest latest-xorg-release)))
  647. (define %kernel.org-updater
  648. (upstream-updater
  649. (name 'kernel.org)
  650. (description "Updater for packages hosted on kernel.org")
  651. (pred (url-prefix-predicate "mirror://kernel.org/"))
  652. (latest latest-kernel.org-release)))
  653. ;;; gnu-maintenance.scm ends here