1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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 ;;; Copyright © 2022 Maxime Devos <maximedevos@telenet.be>
7 ;;; This file is part of GNU Guix.
9 ;;; GNU Guix is free software; you can redistribute it and/or modify it
10 ;;; under the terms of the GNU General Public License as published by
11 ;;; the Free Software Foundation; either version 3 of the License, or (at
12 ;;; your option) any later version.
14 ;;; GNU Guix is distributed in the hope that it will be useful, but
15 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;;; GNU General Public License for more details.
19 ;;; You should have received a copy of the GNU General Public License
20 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22 (define-module (guix gnu-maintenance)
23 #:use-module (web uri)
24 #:use-module (web client)
25 #:use-module (web response)
26 #:use-module (sxml simple)
27 #:use-module (ice-9 regex)
28 #:use-module (ice-9 match)
29 #:use-module (srfi srfi-1)
30 #:use-module (srfi srfi-11)
31 #:use-module (srfi srfi-26)
32 #:use-module (srfi srfi-34)
33 #:use-module (rnrs io ports)
34 #:use-module (system foreign)
35 #:use-module ((guix http-client) #:hide (open-socket-for-uri))
36 ;; not required in many cases, so autoloaded to reduce start-up costs.
37 #:autoload (guix download) (%mirrors)
38 #:use-module (guix ftp-client)
39 #:use-module (guix utils)
40 #:use-module (guix memoization)
41 #:use-module (guix records)
42 #:use-module (guix upstream)
43 #:use-module (guix packages)
44 #:autoload (zlib) (call-with-gzip-input-port)
45 #:autoload (htmlprag) (html->sxml) ;from Guile-Lib
46 #:export (gnu-package-name
47 gnu-package-mundane-name
48 gnu-package-copyright-holder
53 gnu-package-doc-category
54 gnu-package-doc-summary
55 gnu-package-doc-description
57 gnu-package-download-url
68 gnu-release-archive-types
69 gnu-package-name->name+version
77 %generic-html-updater))
81 ;;; Code for dealing with the maintenance of GNU packages, such as
88 ;;; List of GNU packages.
91 (define %gnumaint-base-url
92 "https://web.cvs.savannah.gnu.org/viewvc/*checkout*/www/www/prep/gnumaint/")
94 (define %package-list-url
96 (string-append %gnumaint-base-url "rec/gnupackages.rec")))
98 (define %package-description-url
99 ;; This file contains package descriptions in recutils format.
100 ;; See <https://lists.gnu.org/archive/html/guix-devel/2013-10/msg00071.html>
101 ;; and <https://lists.gnu.org/archive/html/guix-devel/2018-06/msg00362.html>.
103 (string-append %gnumaint-base-url "rec/pkgblurbs.rec")))
105 (define-record-type* <gnu-package-descriptor>
106 gnu-package-descriptor
107 make-gnu-package-descriptor
109 gnu-package-descriptor?
111 (name gnu-package-name)
112 (mundane-name gnu-package-mundane-name)
113 (copyright-holder gnu-package-copyright-holder)
114 (savannah gnu-package-savannah)
115 (fsd gnu-package-fsd)
116 (language gnu-package-language) ; list of strings
117 (logo gnu-package-logo)
118 (doc-category gnu-package-doc-category)
119 (doc-summary gnu-package-doc-summary)
120 (doc-description gnu-package-doc-description) ; taken from 'pkgdescr.txt'
121 (doc-urls gnu-package-doc-urls) ; list of strings
122 (download-url gnu-package-download-url))
124 (define* (official-gnu-packages
125 #:optional (fetch http-fetch/cached))
126 "Return a list of records, which are GNU packages. Use FETCH,
127 to fetch the list of GNU packages over HTTP."
128 (define (read-records port)
129 ;; Return a list of alists. Each alist contains fields of a GNU
131 (let loop ((alist (recutils->alist port))
135 (loop (recutils->alist port)
137 ;; Ignore things like "%rec" (info "(recutils) Record
139 (if (assoc-ref alist "package")
143 (define official-description
144 (let ((db (read-records (fetch %package-description-url #:text? #t))))
146 ;; Return the description found upstream for package NAME, or #f.
147 (and=> (find (lambda (alist)
148 (equal? name (assoc-ref alist "package")))
151 (let ((field (assoc-ref record "blurb")))
152 ;; The upstream description file uses "redirect PACKAGE" as
153 ;; a blurb in cases where the description of the two
154 ;; packages should be considered the same (e.g., GTK+ has
155 ;; "redirect gnome".) This is usually not acceptable for
156 ;; us because we prefer to have distinct descriptions in
157 ;; such cases. Thus, ignore the 'blurb' field when that
160 (not (string-prefix? "redirect " field))
164 (let ((name (assoc-ref alist "package")))
165 (alist->record `(("description" . ,(official-description name))
167 make-gnu-package-descriptor
168 (list "package" "mundane_name" "copyright_holder"
169 "savannah" "fsd" "language" "logo"
170 "doc_category" "doc_summary" "description"
173 '("doc_url" "language"))))
174 (let* ((port (fetch %package-list-url #:text? #t))
175 (lst (read-records port)))
179 (define (find-package name)
180 "Find GNU package called NAME and return it. Return #f if it was not
182 (find (lambda (package)
183 (string=? name (gnu-package-name package)))
184 (official-gnu-packages)))
187 (let ((official-gnu-packages (memoize official-gnu-packages)))
189 "Return true if PACKAGE is a GNU package. This procedure may access the
190 network to check in GNU's database."
191 (define (mirror-type url)
192 (let ((uri (string->uri url)))
193 (and (eq? (uri-scheme uri) 'mirror)
195 ((member (uri-host uri)
196 '("gnu" "gnupg" "gcc" "gnome"))
199 ((equal? (uri-host uri) "cran")
200 ;; Possibly GNU: mirror://cran could be either GNU R itself
201 ;; or a non-GNU package.
204 ;; Definitely non-GNU.
207 (define (gnu-home-page? package)
208 (letrec-syntax ((>> (syntax-rules ()
211 ((_ value proc rest ...)
214 (>> (proc next) rest ...)))))))
215 (>> package package-home-page
218 (member host '("www.gnu.org" "gnu.org"))))))
220 (or (gnu-home-page? package)
221 (match (package-source package)
223 (let ((url (origin-uri origin))
224 (name (package-upstream-name package)))
225 (case (and (string? url) (mirror-type url))
229 (and (member name (map gnu-package-name (official-gnu-packages)))
235 ;;; Latest FTP release.
238 (define (ftp-server/directory package)
239 "Return the FTP server and directory where PACKAGE's tarball are stored."
240 (let ((name (package-upstream-name package)))
241 (values (or (assoc-ref (package-properties package) 'ftp-server)
243 (or (assoc-ref (package-properties package) 'ftp-directory)
244 (string-append "/gnu/" name)))))
247 ;; The .zip extensions is notably used for freefont-ttf.
248 ;; The "-src" pattern is for "TeXmacs-1.0.7.9-src.tar.gz".
249 ;; The "-gnu[0-9]" pattern is for "icecat-38.4.0-gnu1.tar.bz2".
250 ;; Accept underscores as in "PKG_1.2.tar.gz" for some non-GNU packages.
251 ;; Accept 'v' or 'V' prefix as in 'PKG-v2.3.tgz'.
252 (make-regexp "^([^.]+)[-_][vV]?([0-9]|[^-])+(-(src|[sS]ource|gnu[0-9]))?\\.(tar\\.|tgz|zip$)"))
254 (define %alpha-tarball-rx
255 (make-regexp "^.*-.*[0-9](-|~)?(alpha|beta|rc|RC|cvs|svn|git)-?[0-9\\.]*\\.tar\\."))
257 (define (release-file? project file)
258 "Return #f if FILE is not a release tarball of PROJECT, otherwise return
260 (and (not (member (file-extension file)
262 "md5sum" "sha1sum" "sha256sum")))
263 (and=> (regexp-exec %tarball-rx file)
265 ;; Filter out unrelated files, like `guile-www-1.1.1'.
266 ;; Case-insensitive for things like "TeXmacs" vs. "texmacs".
267 ;; The "-src" suffix is for "freefont-src-20120503.tar.gz".
268 (and=> (match:substring match 1)
270 (or (string-ci=? name project)
272 (string-append project
274 (not (regexp-exec %alpha-tarball-rx file))
275 (let ((s (tarball-sans-extension file)))
276 (regexp-exec %package-name-rx s))))
278 (define (tarball->version tarball)
279 "Return the version TARBALL corresponds to. TARBALL is a file name like
280 \"coreutils-8.23.tar.xz\"."
281 (let-values (((name version)
282 (gnu-package-name->name+version
283 (tarball-sans-extension tarball))))
286 (define* (releases project
288 (server "ftp.gnu.org")
289 (directory (string-append "/gnu/" project)))
290 "Return the list of <upstream-release> of PROJECT as a list of release
291 name/directory pairs."
292 ;; TODO: Parse something like fencepost.gnu.org:/gd/gnuorg/packages-ftp.
293 (define conn (ftp-open server))
295 (let loop ((directories (list directory))
300 (coalesce-sources result))
301 ((directory rest ...)
302 (let* ((files (ftp-list conn directory))
303 (subdirs (filter-map (match-lambda
304 ((name 'directory . _) name)
307 (define (file->url file)
308 (string-append "ftp://" server directory "/" file))
310 (define (file->source file)
311 (let ((url (file->url file)))
314 (version (tarball->version file))
316 (signature-urls (list (string-append url ".sig"))))))
318 (loop (append (map (cut string-append directory "/" <>)
322 ;; Filter out signatures, deltas, and files which
323 ;; are potentially not releases of PROJECT--e.g.,
324 ;; in /gnu/guile, filter out guile-oops and
325 ;; guile-www; in mit-scheme, filter out binaries.
326 (filter-map (match-lambda
328 (and (release-file? project file)
329 (file->source file)))
334 (define* (latest-ftp-release project
336 (server "ftp.gnu.org")
337 (directory (string-append "/gnu/" project))
338 (file->signature (cut string-append <> ".sig")))
339 "Return an <upstream-source> for the latest release of PROJECT on SERVER
340 under DIRECTORY, or #f. Use FTP-OPEN and FTP-CLOSE to open (resp. close) FTP
341 connections; this can be useful to reuse connections.
343 FILE->SIGNATURE must be a procedure; it is passed a source file URL and must
344 return the corresponding signature URL, or #f it signatures are unavailable."
346 (if (version>? a b) a b))
348 (define (latest-release a b)
349 (if (version>? (upstream-source-version a) (upstream-source-version b))
352 (define patch-directory-name?
353 ;; Return #t for patch directory names such as 'bash-4.2-patches'.
354 (cut string-suffix? "patches" <>))
356 (define conn (ftp-open server #:timeout 5))
358 (define (file->url directory file)
359 (string-append "ftp://" server directory "/" file))
361 (define (file->source directory file)
362 (let ((url (file->url directory file)))
365 (version (tarball->version file))
366 ;; uri-mirror-rewrite: Don't turn nice mirror:// URIs into ftp://
367 ;; URLs during "guix refresh -u".
368 (urls (list (uri-mirror-rewrite url)))
369 (signature-urls (match (file->signature url)
371 (sig (list (uri-mirror-rewrite sig))))))))
373 (let loop ((directory directory)
375 (let* ((entries (catch 'ftp-error
376 (lambda _ (ftp-list conn directory))
379 ;; Filter out things like /gnupg/patches. Filter out "w32"
380 ;; directories as found on ftp.gnutls.org.
381 (subdirs (filter-map (match-lambda
382 (((? patch-directory-name? dir)
385 (("w32" 'directory . _)
387 (("unstable" 'directory . _)
388 ;; As seen at ftp.gnupg.org/gcrypt/pinentry.
390 ((directory 'directory . _)
395 ;; Whether or not SUBDIRS is empty, compute the latest releases
396 ;; for the current directory. This is necessary for packages
397 ;; such as 'sharutils' that have a sub-directory that contains
398 ;; only an older release.
399 (releases (filter-map (match-lambda
401 (and (release-file? project file)
402 (file->source directory file)))
406 ;; Assume that SUBDIRS correspond to versions, and jump into the
407 ;; one with the highest version number.
408 (let* ((release (reduce latest-release #f
409 (coalesce-sources releases)))
410 (result (if (and result release)
411 (latest-release release result)
412 (or release result)))
413 (target (reduce latest #f subdirs)))
415 (loop (string-append directory "/" target)
421 (define* (latest-release package
423 (server "ftp.gnu.org")
424 (directory (string-append "/gnu/" package)))
425 "Return the <upstream-source> for the latest version of PACKAGE or #f.
426 PACKAGE must be the canonical name of a GNU package."
427 (latest-ftp-release package
429 #:directory directory))
431 (define-syntax-rule (false-if-ftp-error exp)
432 "Return #f if an FTP error is raise while evaluating EXP; return the result
437 (lambda (key port . rest)
438 (if (ftp-connection? port)
443 (define (latest-release* package)
444 "Like 'latest-release', but (1) take a <package> object, and (2) ignore FTP
445 errors that might occur when PACKAGE is not actually a GNU package, or not
446 hosted on ftp.gnu.org, or not under that name (this is the case for
447 \"emacs-auctex\", for instance.)"
448 (let-values (((server directory)
449 (ftp-server/directory package)))
450 (false-if-ftp-error (latest-release (package-upstream-name package)
452 #:directory directory))))
456 ;;; Latest HTTP release.
459 (define (html-links sxml)
460 "Return the list of links found in SXML, the SXML tree of an HTML page."
461 (let loop ((sxml sxml)
464 (('a ('@ attributes ...) body ...)
465 (match (assq 'href attributes)
466 (#f (fold loop links body))
467 (('href url) (fold loop (cons url links) body))))
468 ((tag ('@ _ ...) body ...)
469 (fold loop links body))
471 (fold loop links body))
475 (define* (latest-html-release package
477 (base-url "https://kernel.org/pub")
478 (directory (string-append "/" package))
480 "Return an <upstream-source> for the latest release of PACKAGE (a string) on
481 SERVER under DIRECTORY, or #f. BASE-URL should be the URL of an HTML page,
482 typically a directory listing as found on 'https://kernel.org/pub'.
484 When FILE->SIGNATURE is omitted or #f, guess the detached signature file name,
485 if any. Otherwise, FILE->SIGNATURE must be a procedure; it is passed a source
486 file URL and must return the corresponding signature URL, or #f it signatures
488 (let* ((uri (string->uri (if (string-null? directory)
490 (string-append base-url directory "/"))))
491 (port (http-fetch/cached uri #:ttl 3600))
492 (sxml (html->sxml port))
493 (links (delete-duplicates (html-links sxml))))
494 (define (file->signature/guess url)
495 (let ((base (basename url)))
497 (any (lambda (extension)
498 (and (string=? (string-append base extension)
500 (string-append url extension)))
501 '(".asc" ".sig" ".sign")))
504 (define (url->release url)
505 (let* ((base (basename url))
506 (base-url (string-append base-url directory))
507 (url (cond ((and=> (string->uri url) uri-scheme) ;full URL?
509 ;; full URL, except for URI scheme. Reuse the URI
510 ;; scheme of the document that contains the link.
511 ((string-prefix? "//" url)
513 (symbol->string (uri-scheme (string->uri base-url)))
515 ((string-prefix? "/" url) ;absolute path?
516 (let ((uri (string->uri base-url)))
518 (build-uri (uri-scheme uri)
519 #:host (uri-host uri)
520 #:port (uri-port uri)
523 ;; URL is a relative path and BASE-URL may or may not
525 ((string-suffix? "/" base-url)
526 (string-append base-url url))
528 ;; If DIRECTORY is non-empty, assume BASE-URL
529 ;; denotes a directory; otherwise, assume BASE-URL
530 ;; denotes a file within a directory, and that URL
531 ;; is relative to that directory.
532 (string-append (if (string-null? directory)
536 (and (release-file? package base)
537 (let ((version (tarball->version base)))
541 ;; uri-mirror-rewrite: Don't turn nice mirror:// URIs into ftp://
542 ;; URLs during "guix refresh -u".
543 (urls (list (uri-mirror-rewrite url)))
545 (and=> ((or file->signature file->signature/guess) url)
546 (lambda (url) (list (uri-mirror-rewrite url))))))))))
549 (filter-map url->release links))
555 ;; Select the most recent release and return it.
556 (reduce (lambda (r1 r2)
557 (if (version>? (upstream-source-version r1)
558 (upstream-source-version r2))
561 (coalesce-sources candidates))))))
568 (define %gnu-file-list-uri
569 ;; URI of the file list for ftp.gnu.org.
570 (string->uri "https://ftp.gnu.org/find.txt.gz"))
572 (define ftp.gnu.org-files
574 "Return the list of files available at ftp.gnu.org."
576 ;; XXX: Memoize the whole procedure to work around the fact that
577 ;; 'http-fetch/cached' caches the gzipped version.
579 (define (trim-leading-components str)
580 ;; Trim the leading ".", if any, in "./gnu/foo".
581 (string-trim str (char-set #\.)))
583 (define (string->lines str)
584 (string-tokenize str (char-set-complement (char-set #\newline))))
586 ;; Since https://ftp.gnu.org honors 'If-Modified-Since', the hard-coded
587 ;; TTL can be relatively short.
588 (let ((port (http-fetch/cached %gnu-file-list-uri #:ttl (* 15 60))))
589 (map trim-leading-components
590 (call-with-gzip-input-port port
591 (compose string->lines get-string-all))))))
593 (define (latest-gnu-release package)
594 "Return the latest release of PACKAGE, a GNU package available via
597 This method does not rely on FTP access at all; instead, it browses the file
598 list available from %GNU-FILE-LIST-URI over HTTP(S)."
599 (let-values (((server directory)
600 (ftp-server/directory package))
602 (package-upstream-name package)))
603 (let* ((files (ftp.gnu.org-files))
604 (relevant (filter (lambda (file)
605 (and (string-prefix? "/gnu" file)
606 (string-contains file directory)
607 (release-file? name (basename file))))
609 (match (sort relevant (lambda (file1 file2)
610 (version>? (tarball-sans-extension
612 (tarball-sans-extension
614 ((and tarballs (reference _ ...))
615 (let* ((version (tarball->version reference))
616 (tarballs (filter (lambda (file)
617 (string=? (tarball-sans-extension
619 (tarball-sans-extension
620 (basename reference))))
625 (urls (map (lambda (file)
626 (string-append "mirror://gnu/"
628 (string-length "/gnu/"))))
630 (signature-urls (map (cut string-append <> ".sig") urls)))))
634 (define %package-name-rx
635 ;; Regexp for a package name, e.g., "foo-X.Y". Since TeXmacs uses
636 ;; "TeXmacs-X.Y-src", the `-src' suffix is allowed.
637 (make-regexp "^(.*)[-_][vV]?(([0-9]|\\.)+)(-src|\\.src|\\.orig)?"))
639 (define (gnu-package-name->name+version name+version)
640 "Return the package name and version number extracted from NAME+VERSION."
641 (let ((match (regexp-exec %package-name-rx name+version)))
643 (values name+version #f)
644 (values (match:substring match 1) (match:substring match 2)))))
646 (define gnome-package?
647 (url-prefix-predicate "mirror://gnome/"))
649 (define (pure-gnu-package? package)
650 "Return true if PACKAGE is a non-Emacs and non-GNOME GNU package. This
651 excludes AucTeX, for instance, whose releases are now uploaded to
652 elpa.gnu.org, GNU Radio, which has releases at www.gnuradio.org, and all the
653 GNOME packages; EMMS is included though, because its releases are on gnu.org."
654 (and (or (not (string-prefix? "emacs-" (package-name package)))
655 (gnu-hosted? package))
656 (not (gnome-package? package))
657 (not (string-prefix? "gnuradio" (package-name package)))
658 (gnu-package? package)))
661 (url-prefix-predicate "mirror://gnu/"))
663 (define (uri-mirror-rewrite uri)
664 "Rewrite URI to a mirror:// URI if possible, or return URI unmodified."
665 (if (string-prefix? "mirror://" uri)
666 uri ;nothing to do, it's already a mirror URI
667 (let loop ((mirrors %mirrors))
671 (((mirror-id mirror-urls ...) rest ...)
672 (match (find (cut string-prefix? <> uri) mirror-urls)
676 (format #f "mirror://~a/~a"
678 (string-drop uri (string-length prefix))))))))))
680 (define %savannah-base
681 ;; One of the Savannah mirrors listed at
682 ;; <http://download0.savannah.gnu.org/mirmon/savannah/> that serves valid
683 ;; HTML (unlike <https://download.savannah.nongnu.org/releases>.)
684 "https://nongnu.freemirror.org/nongnu")
686 (define (latest-savannah-release package)
687 "Return the latest release of PACKAGE."
688 (let* ((uri (string->uri
689 (match (origin-uri (package-source package))
690 ((? string? uri) uri)
691 ((uri mirrors ...) uri))))
692 (package (package-upstream-name package))
693 (directory (dirname (uri-path uri))))
694 ;; Note: We use the default 'file->signature', which adds ".sig", ".asc",
695 ;; or whichever detached signature naming scheme PACKAGE uses.
696 (latest-html-release package
697 #:base-url %savannah-base
698 #:directory directory)))
700 (define (latest-sourceforge-release package)
701 "Return the latest release of PACKAGE."
702 (define (uri-append uri extension)
703 ;; Return URI with EXTENSION appended.
704 (build-uri (uri-scheme uri)
705 #:host (uri-host uri)
706 #:path (string-append (uri-path uri) extension)))
708 (define (valid-uri? uri port)
709 ;; Return true if URI is reachable.
711 (case (response-code (http-head uri #:port port #:keep-alive? #t))
715 (let* ((name (package-upstream-name package))
716 (base (string-append "https://sourceforge.net/projects/"
718 (url (string-append base "/latest/download"))
719 (uri (string->uri url))
720 (port (false-if-exception (open-socket-for-uri uri)))
722 (http-head uri #:port port #:keep-alive? #t))))
727 (= 302 (response-code response))
728 (response-location response)
729 (match (string-tokenize (uri-path (response-location response))
730 (char-set-complement (char-set #\/)))
732 (let* ((path (string-join components "/"))
733 (url (string-append "mirror://sourceforge/" path)))
734 (and (release-file? name (basename path))
736 ;; Take the heavy-handed approach of probing 3 additional
737 ;; URLs. XXX: Would be nicer if this could be avoided.
738 (let* ((loc (response-location response))
739 (sig (any (lambda (extension)
740 (let ((uri (uri-append loc extension)))
741 (and (valid-uri? uri port)
742 (string-append url extension))))
743 '(".asc" ".sig" ".sign"))))
746 (version (tarball->version (basename path)))
748 (signature-urls (and sig (list sig)))))))))))
751 (close-port port))))))
753 (define (latest-xorg-release package)
754 "Return the latest release of PACKAGE."
755 (let ((uri (string->uri (origin-uri (package-source package)))))
758 (package-name package)
759 #:server "ftp.freedesktop.org"
761 (string-append "/pub/xorg/" (dirname (uri-path uri)))))))
763 (define (latest-kernel.org-release package)
764 "Return the latest release of PACKAGE, the name of a kernel.org package."
765 (define %kernel.org-base
766 ;; This URL and sub-directories thereof are nginx-generated directory
767 ;; listings suitable for 'latest-html-release'.
768 "https://mirrors.edge.kernel.org/pub")
770 (define (file->signature file)
771 (string-append (file-sans-extension file) ".sign"))
773 (let* ((uri (string->uri
774 (match (origin-uri (package-source package))
775 ((? string? uri) uri)
776 ((uri mirrors ...) uri))))
777 (package (package-upstream-name package))
778 (directory (dirname (uri-path uri))))
779 (latest-html-release package
780 #:base-url %kernel.org-base
781 #:directory directory
782 #:file->signature file->signature)))
784 (define html-updatable-package?
785 ;; Return true if the given package may be handled by the generic HTML
787 (let ((hosting-sites '("github.com" "github.io" "gitlab.com"
788 "notabug.org" "sr.ht" "gitlab.inria.fr"
789 "ftp.gnu.org" "download.savannah.gnu.org"
790 "pypi.org" "crates.io" "rubygems.org"
791 "bioconductor.org")))
793 (url-predicate (lambda (url)
794 (match (string->uri url)
797 (let ((scheme (uri-scheme uri))
798 (host (uri-host uri)))
799 (and (memq scheme '(http https))
800 (not (member host hosting-sites)))))))))
803 (or (assoc-ref (package-properties package) 'release-monitoring-url)
804 (http-url? package)))))
806 (define (latest-html-updatable-release package)
807 "Return the latest release of PACKAGE. Do that by crawling the HTML page of
808 the directory containing its source tarball."
809 (let* ((uri (string->uri
810 (match (origin-uri (package-source package))
811 ((? string? url) url)
813 (custom (assoc-ref (package-properties package)
814 'release-monitoring-url))
816 (string-append (symbol->string (uri-scheme uri))
817 "://" (uri-host uri))))
818 (directory (if custom
820 (dirname (uri-path uri))))
821 (package (package-upstream-name package)))
824 (guard (c ((http-get-error? c) #f))
825 (latest-html-release package
827 #:directory directory)))
829 ;; Return false and move on upon connection failures and bogus HTTP
831 (unless (memq key '(gnutls-error tls-certificate-error
833 bad-header bad-header-component))
834 (apply throw key args))
838 ;; This is for everything at ftp.gnu.org.
841 (description "Updater for GNU packages")
843 (latest latest-gnu-release)))
845 (define %gnu-ftp-updater
846 ;; This is for GNU packages taken from alternate locations, such as
847 ;; alpha.gnu.org, ftp.gnupg.org, etc. It is obsolescent.
850 (description "Updater for GNU packages only available via FTP")
851 (pred (lambda (package)
852 (and (not (gnu-hosted? package))
853 (pure-gnu-package? package))))
854 (latest latest-release*)))
856 (define %savannah-updater
859 (description "Updater for packages hosted on savannah.gnu.org")
860 (pred (url-prefix-predicate "mirror://savannah/"))
861 (latest latest-savannah-release)))
863 (define %sourceforge-updater
866 (description "Updater for packages hosted on sourceforge.net")
867 (pred (url-prefix-predicate "mirror://sourceforge/"))
868 (latest latest-sourceforge-release)))
870 (define %xorg-updater
873 (description "Updater for X.org packages")
874 (pred (url-prefix-predicate "mirror://xorg/"))
875 (latest latest-xorg-release)))
877 (define %kernel.org-updater
880 (description "Updater for packages hosted on kernel.org")
881 (pred (url-prefix-predicate "mirror://kernel.org/"))
882 (latest latest-kernel.org-release)))
884 (define %generic-html-updater
887 (description "Updater that crawls HTML pages.")
888 (pred html-updatable-package?)
889 (latest latest-html-updatable-release)))
891 ;;; gnu-maintenance.scm ends here