;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2014 David Thompson ;;; Copyright © 2015, 2017, 2019, 2020, 2021 Ricardo Wurmus ;;; Copyright © 2015, 2016, 2017 Leo Famulari ;;; Copyright © 2016 Hartmut Goebel ;;; Copyright © 2016-2019, 2022 Marius Bakke ;;; Copyright © 2017 Danny Milosavljevic ;;; Copyright © 2017, 2018, 2020 Tobias Geerinckx-Rice ;;; Copyright © 2017 Frederick M. Muriithi ;;; Copyright © 2017 Christine Lemmer-Webber ;;; Copyright © 2017 Julien Lepiller ;;; Copyright © 2019, 2021, 2022 Efraim Flashner ;;; Copyright © 2019, 2021 Nicolas Goaziou ;;; Copyright © 2019 Alexandros Theodotou ;;; Copyright © 2019 Brett Gilio ;;; Copyright © 2020
aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
;;; Copyright © 2015, 2018 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2018 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2018, 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
;;; Copyright © 2020 Christine Lemmer-Webber <cwebber@dustycloud.org>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2024 Nicolas Graves <ngraves@ngraves.fr>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (gnu build activation)
  #:use-module (gnu system accounts)
  #:use-module (gnu system privilege)
  #:use-module (gnu build accounts)
  #:use-module (gnu build linux-boot)
  #:use-module (guix build utils)
  #:use-module ((guix build syscalls) #:select (with-file-lock))
  #:use-module (ice-9 ftw)
  #:use-module (ice-9 match)
  #:use-module (ice-9 vlist)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-11)
  #:use-module (srfi srfi-26)
  #:export (activate-users+groups
            activate-user-home
            activate-etc
            activate-privileged-programs
            activate-special-files
            activate-modprobe
            activate-firmware
            activate-ptrace-attach
            activate-current-system
            mkdir-p/perms))

;;; Commentary:
;;;
;;; This module provides "activation" helpers.  Activation is the process that
;;; consists in setting up system-wide files and directories so that an
;;; 'operating-system' configuration becomes active.
;;;
;;; Code:

(define %skeleton-directory
  ;; Directory containing skeleton files for new accounts.
  ;; Note: keep the trailing '/' so that 'scandir' enters it.
  "/etc/skel/")

(define (dot-or-dot-dot? file)
  (member file '("." "..")))

;; Based upon mkdir-p from (guix build utils)
(define (verify-not-symbolic dir)
  "Verify DIR or its ancestors aren't symbolic links."
  (define absolute?
    (string-prefix? "/" dir))

  (define not-slash
    (char-set-complement (char-set #\/)))

  (define (verify-component file)
    (unless (eq? 'directory (stat:type (lstat file)))
      (error "file name component is not a directory" dir)))

  (let loop ((components (string-tokenize dir not-slash))
             (root       (if absolute?
                             ""
                             ".")))
    (match components
      ((head tail ...)
       (let ((file (string-append root "/" head)))
         (catch 'system-error
           (lambda ()
             (verify-component file)
             (loop tail file))
           (lambda args
             (if (= ENOENT (system-error-errno args))
                 #t
                 (apply throw args))))))
      (() #t))))

;; TODO: the TOCTTOU race can be addressed once guile has bindings
;; for fstatat, openat and friends.
(define (mkdir-p/perms directory owner bits)
  "Create the directory DIRECTORY and all its ancestors.
Verify no component of DIRECTORY is a symbolic link.
Warning: this is currently suspect to a TOCTTOU race!"
  (verify-not-symbolic directory)
  (mkdir-p directory)
  (chown directory (passwd:uid owner) (passwd:gid owner))
  (chmod directory bits))

(define* (copy-account-skeletons home
                                 #:key
                                 (directory %skeleton-directory)
                                 uid gid)
  "Copy the account skeletons from DIRECTORY to HOME.  When UID is an integer,
make it the owner of all the files created except the home directory; likewise
for GID."
  (define (set-owner file)
    (when (or uid gid)
      (chown file (or uid -1) (or gid -1))))

  (let ((files (scandir directory (negate dot-or-dot-dot?)
                        string<?)))
    (mkdir-p home)
    (for-each (lambda (file)
                (let ((target (string-append home "/" file)))
                  (copy-recursively (string-append directory "/" file)
                                    target
                                    #:log (%make-void-port "w"))
                  (for-each set-owner
                            (find-files target (const #t)
                                        #:directories? #t))
                  (make-file-writable target)))
              files)))

(define* (make-skeletons-writable home
                                  #:optional (directory %skeleton-directory))
  "Make sure that the files that have been copied from DIRECTORY to HOME are
owner-writable in HOME."
  (let ((files (scandir directory (negate dot-or-dot-dot?)
                        string<?)))
    (for-each (lambda (file)
                (let ((target (string-append home "/" file)))
                  (when (file-exists? target)
                    (make-file-writable target))))
              files)))

(define (duplicates lst)
  "Return elements from LST present more than once in LST."
  (let loop ((lst lst)
             (seen vlist-null)
             (result '()))
    (match lst
      (()
       (reverse result))
      ((head . tail)
       (loop tail
             (vhash-cons head #t seen)
             (if (vhash-assoc head seen)
                 (cons head result)
                 result))))))

(define (activate-users+groups users groups)
  "Make sure USERS (a list of user account records) and GROUPS (a list of user
group records) are all available."
  (define (make-home-directory user)
    (let ((home (user-account-home-directory user))
          (pwd  (getpwnam (user-account-name user))))
      (mkdir-p home)

      ;; Always set ownership and permissions for home directories of system
      ;; accounts.  If a service needs looser permissions on its home
      ;; directories, it can always chmod it in an activation snippet.
      (chown home (passwd:uid pwd) (passwd:gid pwd))
      (chmod home #o700)))

  (define system-accounts
    (filter (lambda (user)
              (and (user-account-system? user)
                   (user-account-create-home-directory? user)))
            users))

  ;; Allow home directories to be created under /var/lib.
  (mkdir-p "/var/lib")

  ;; Take same lock as libc's 'lckpwdf' (but without a timeout) while we read
  ;; and write the databases.  This ensures there's no race condition with
  ;; other tools that might be accessing it at the same time.
  (with-file-lock %password-lock-file
    (let-values (((groups passwd shadow)
                  (user+group-databases users groups)))
      (write-group groups)
      (write-passwd passwd)
      (write-shadow shadow)))

  ;; Home directories of non-system accounts are created by
  ;; 'activate-user-home'.
  (for-each make-home-directory system-accounts)

  ;; Turn shared home directories, such as /var/empty, into root-owned,
  ;; read-only places.
  (for-each (lambda (directory)
              (chown directory 0 0)
              (chmod directory #o555))
            (duplicates (map user-account-home-directory system-accounts))))

(define (activate-user-home users)
  "Create and populate the home directory of USERS, a list of tuples, unless
they already exist."
  (define ensure-user-home
    (lambda (user)
      (let ((name         (user-account-name user))
            (home         (user-account-home-directory user))
            (create-home? (user-account-create-home-directory? user))
            (system?      (user-account-system? user)))
        ;; The home directories of system accounts are created during
        ;; activation, not here.
        (unless (or (not home) (not create-home?) system?
                    (directory-exists? home))
          (let* ((pw  (getpwnam name))
                 (uid (passwd:uid pw))
                 (gid (passwd:gid pw)))
            (mkdir-p home)
            (chmod home #o700)
            (copy-account-skeletons home
                                    #:uid uid #:gid gid)

            ;; It is important 'chown' be called after
            ;; 'copy-account-skeletons'.  Otherwise, a malicious user with
            ;; good timing could create a symlink in HOME that would be
            ;; dereferenced by 'copy-account-skeletons'.
            (chown home uid gid))))))

  (for-each ensure-user-home users))

(define (activate-etc etc)
  "Install ETC, a directory in the store, as the source of static files for
/etc."

  ;; /etc is a mixture of static and dynamic settings.  Here is where we
  ;; initialize it from the static part.

  (define (rm-f file)
    (false-if-exception (delete-file file)))

  (format #t "populating /etc from ~a...~%" etc)
  (mkdir-p "/etc")

  ;; Create the /etc/ssl -> /run/current-system/profile/etc/ssl symlink.  This
  ;; symlink, to a target outside of the store, probably doesn't belong in the
  ;; static 'etc' store directory.  However, if it were to be put there,
  ;; beware that if /run/current-system/profile/etc/ssl doesn't exist at the
  ;; time of activation (e.g. when installing a fresh system), the call to
  ;; 'file-is-directory?' below will fail because it uses 'stat', not 'lstat'.
  (rm-f "/etc/ssl")
  (symlink "/run/current-system/profile/etc/ssl" "/etc/ssl")

  (rm-f "/etc/static")
  (symlink etc "/etc/static")
  (for-each (lambda (file)
              (let ((target (string-append "/etc/" file))
                    (source (string-append "/etc/static/" file)))
                (rm-f target)

                ;; Things such as /etc/sudoers must be regular files, not
                ;; symlinks; furthermore, they could be modified behind our
                ;; back---e.g., with 'visudo'.  Thus, make a copy instead of
                ;; symlinking them.
                (if (file-is-directory? source)
                    (symlink source target)
                    (copy-file source target))

                ;; XXX: Dirty hack to meet sudo's expectations.
                (when (string=? (basename target) "sudoers")
                  (chmod target #o440))))
            (scandir etc (negate dot-or-dot-dot?)

                     ;; The default is 'string-locale<?', but we don't have
                     ;; it when run from the initrd's statically-linked
                     ;; Guile.
                     string<?)))

(define %setuid-directory
  ;; Place where setuid programs used to be stored.  It exists for backwards
  ;; compatibility & will be removed.  Use %PRIVILEGED-PROGRAM-DIRECTORY instead.
  "/run/setuid-programs")

(define %privileged-program-directory
  ;; Place where privileged copies of programs are stored.
  "/run/privileged/bin")

(define (activate-privileged-programs programs libcap)
  "Turn PROGRAMS, a list of file privileged-programs records, into privileged
copies stored under %PRIVILEGED-PROGRAM-DIRECTORY, using LIBCAP's setcap(8)
binary if needed."
  (define (ensure-empty-directory directory)
    (if (file-exists? directory)
        (for-each (compose delete-file
                           (cut string-append directory "/" <>))
                  (scandir directory
                           (lambda (file)
                             (not (member file '("." ".."))))
                           string<?))
        (mkdir-p directory))    )

  (define (make-privileged-program program setuid? setgid? uid gid capabilities)
    (let ((target (string-append %privileged-program-directory
                                 "/" (basename program)))
          (mode (+ #o0555                   ; base permissions
                   (if setuid? #o4000 0)    ; setuid bit
                   (if setgid? #o2000 0)))) ; setgid bit
      (copy-file program target)
      (chown target uid gid)
      (chmod target mode)
      (when (and capabilities libcap)
        (system* (string-append libcap "/sbin/setcap")
                 "-q" capabilities target))))

  (define (make-deprecated-wrapper program)
    ;; This will eventually become a script that warns on usage, then vanish.
    (symlink (string-append %privileged-program-directory
                            "/" (basename program))
             (string-append %setuid-directory
                            "/" (basename program))))

  (format #t "setting up privileged programs in '~a'...~%"
          %privileged-program-directory)
  (ensure-empty-directory %privileged-program-directory)
  (ensure-empty-directory %setuid-directory)

  (for-each (lambda (program)
              (catch 'system-error
                (lambda ()
                  (let* ((program-name (privileged-program-program program))
                         (setuid?      (privileged-program-setuid? program))
                         (setgid?      (privileged-program-setgid? program))
                         (user         (privileged-program-user program))
                         (group        (privileged-program-group program))
                         (capabilities (privileged-program-capabilities program))
                         (uid (match user
                                ((? string?) (passwd:uid (getpwnam user)))
                                ((? integer?) user)))
                         (gid (match group
                                ((? string?) (group:gid (getgrnam group)))
                                ((? integer?) group))))
                    (make-privileged-program program-name
                                             setuid? setgid? uid gid
                                             capabilities)
                    (make-deprecated-wrapper program-name)))
                (lambda args
                  ;; If we fail to create a privileged program, better keep going
                  ;; so that we don't leave %PRIVILEGED-PROGRAM-DIRECTORY empty
                  ;; or half-populated.  This can happen if PROGRAMS contains
                  ;; incorrect file names: <https://bugs.gnu.org/38800>.
                  (format (current-error-port)
                          "warning: failed to privilege ~s: ~a~%"
                          (privileged-program-program program)
                          (strerror (system-error-errno args))))))
            programs))

(define (activate-special-files special-files)
  "Install the files listed in SPECIAL-FILES.  Each element of SPECIAL-FILES
is a pair where the first element is the name of the special file and the
second element is the name it should appear at, such as:

  ((\"/bin/sh\" \"/gnu/store/…-bash/bin/sh\")
   (\"/usr/bin/env\" \"/gnu/store/…-coreutils/bin/env\"))
"
  (define install-special-file
    (match-lambda
      ((target file)
       (let ((pivot (string-append target ".new")))
         (mkdir-p (dirname target))
         (symlink file pivot)
         (rename-file pivot target)))))

  (for-each install-special-file special-files))

(define (activate-modprobe modprobe)
  "Tell the kernel to use MODPROBE to load modules."

  ;; If the kernel was built without loadable module support, this file is
  ;; unavailable, so check for its existence first.
  (when (file-exists? "/proc/sys/kernel/modprobe")
    (call-with-output-file "/proc/sys/kernel/modprobe"
      (lambda (port)
        (display modprobe port)))))

(define (activate-firmware directory)
  "Tell the kernel to look for device firmware under DIRECTORY.  This
mechanism bypasses udev: it allows Linux to handle firmware loading directly
by itself, without having to resort to a \"user helper\"."

  ;; If the kernel was built without firmware loading support, this file
  ;; does not exist.  Do nothing in that case.
  (let ((firmware-path "/sys/module/firmware_class/parameters/path"))
    (when (file-exists? firmware-path)
      (call-with-output-file firmware-path
        (lambda (port)
          (display directory port))))))

(define (activate-ptrace-attach)
  "Allow users to PTRACE_ATTACH their own processes.

This works around a regression introduced in the default \"security\" policy
found in Linux 3.4 onward that prevents users from attaching to their own
processes--see Yama.txt in the Linux source tree for the rationale.  This
sounds like an unacceptable restriction for little or no security
improvement."
  (let ((file "/proc/sys/kernel/yama/ptrace_scope"))
    (when (file-exists? file)
      (call-with-output-file file
        (lambda (port)
          (display 0 port))))))


(define %current-system
  ;; The system that is current (a symlink.)  This is not necessarily the same
  ;; as the system we booted (aka. /run/booted-system) because we can re-build
  ;; a new system configuration and activate it, without rebooting.
  "/run/current-system")

(define (boot-time-system)
  "Return the 'gnu.system' argument passed on the kernel command line."
  (find-long-option "gnu.system" (if (string-contains %host-type "linux-gnu")
                                   (linux-command-line)
                                   (command-line))))

(define* (activate-current-system
          #:optional (system (or (getenv "GUIX_NEW_SYSTEM")
                                 (boot-time-system))))
  "Atomically make SYSTEM the current system."
  ;; The 'GUIX_NEW_SYSTEM' environment variable is used as a way for 'guix
  ;; system reconfigure' to pass the file name of the new system.

  (format #t "making '~a' the current system...~%" system)

  (mkdir-p "/run")
  ;; Atomically make SYSTEM current.
  (let ((new (string-append %current-system ".new")))
    (symlink system new)
    (rename-file new %current-system)))

;;; activation.scm ends here
(define-public python-sphinx-me (package (name "python-sphinx-me") (version "0.3") (source (origin (method url-fetch) (uri (pypi-uri "sphinx-me" version)) (sha256 (base32 "06jzgp213zihnvpcy2y5jy3ykid3apc2ncp2pg6a2g05lhiziglq")))) (build-system python-build-system) (home-page "https://github.com/stephenmcd/sphinx-me") (synopsis "Create a Sphinx documentation shell") (description "Create a Sphinx documentation shell for your project and include the README file as the documentation index. It handles extracting the required meta data such as the project name, author and version from your project for use in your Sphinx docs.") (license license:bsd-2))) (define-public python-sphinx-repoze-autointerface (package (name "python-sphinx-repoze-autointerface") (version "0.8") (source (origin (method url-fetch) (uri (pypi-uri "repoze.sphinx.autointerface" version)) (sha256 (base32 "08ycivzf7bh4a1zcyp31hbyqs1b2c9r26raa3vxjwwmbfqr3iw4f")))) (build-system python-build-system) (arguments '(#:tests? #f)) ; No tests. (propagated-inputs (list python-sphinx python-zope-interface)) (synopsis "Auto-generate Sphinx API docs from Zope interfaces") (description "This package defines an extension for the Sphinx documentation system. The extension allows generation of API documentation by introspection of @code{zope.interface} instances in code.") (home-page "https://github.com/repoze/repoze.sphinx.autointerface") (license license:repoze))) (define-public python-sphinx-prompt (package (name "python-sphinx-prompt") (version "1.5.0") (source (origin (method git-fetch) ; no source release in PyPI (uri (git-reference (url "https://github.com/sbrunner/sphinx-prompt") (commit version))) (file-name (git-file-name name version)) (sha256 (base32 "0x9wmgf04rzivbzp7jv1b7fkhkpi02lpk5w1qf4i7bcgih00ym8a")))) (build-system python-build-system) (arguments `(#:phases (modify-phases %standard-phases (replace 'check (lambda* (#:key inputs outputs tests? #:allow-other-keys) (when tests? (add-installed-pythonpath inputs outputs) (invoke "python" "-m" "pytest"))))))) (native-inputs (list python-pytest python-sphinx)) (home-page "https://github.com/sbrunner/sphinx-prompt") (synopsis "Sphinx directive to add unselectable prompt") (description "This package provides a Sphinx directive to add unselectable prompt.") (license license:bsd-3))) (define-public python-sphinx-alabaster-theme (package (name "python-sphinx-alabaster-theme") (version "0.7.12") (source (origin (method url-fetch) (uri (pypi-uri "alabaster" version)) (sha256 (base32 "00nwwjj2d2ym4s2kk217x7jkx1hnczc3fvm8yxbqmsp6b0nxfqd6")))) (build-system python-build-system) (propagated-inputs (list python-pygments)) (home-page "https://alabaster.readthedocs.io/") (synopsis "Configurable sidebar-enabled Sphinx theme") (description "Alabaster is a visually (c)lean, responsive, configurable theme for the Sphinx documentation system. It's the default theme of Sphinx.") (license license:bsd-3))) (define-public python-sphinx-argparse (package (name "python-sphinx-argparse") (version "0.3.1") (source (origin (method url-fetch) (uri (pypi-uri "sphinx-argparse" version)) (sha256 (base32 "07nw68nrbpzsswb5bz8gdb5allgj6jnz8m81afhr9v6c8fyiq5c2")))) (build-system python-build-system) (propagated-inputs (list python-sphinx)) (native-inputs (list python-commonmark python-pytest python-sphinx-rtd-theme)) (home-page "https://github.com/ribozz/sphinx-argparse") (synopsis "Sphinx extension for documenting argparse commands and options") (description "This package is a sphinx extension that automatically documents argparse commands and options") (license license:expat))) ;;; FIXME: Currently broken by Jinja >= 3.10 (see: ;;; https://foss.heptapod.net/doc-utils/cloud_sptheme/-/issues/47). (define-public python-sphinx-cloud-sptheme (package (name "python-sphinx-cloud-sptheme") (version "1.10.1") (source (origin (method hg-fetch) (uri (hg-reference (url "https://foss.heptapod.net/doc-utils/cloud_sptheme") (changeset version))) (file-name (hg-file-name name version)) (sha256 (base32 "0k0pgi0vcn8vdy3k6x11fpp4mqp7p3l6n6pjfi3mir3vwjhdfz7l")))) (build-system python-build-system) (native-inputs (list python-mock)) (propagated-inputs (list python-sphinx)) (home-page "https://foss.heptapod.net/doc-utils/cloud_sptheme") (synopsis "Cloud theme for Sphinx") (description "This package contains the @emph{Cloud} theme for Sphinx and some related extensions.") (license license:bsd-3))) (define-public python-guzzle-sphinx-theme (package (name "python-guzzle-sphinx-theme") (version "0.7.11") (source (origin (method url-fetch) (uri (pypi-uri "guzzle_sphinx_theme" version)) (sha256 (base32 "1rnkzrrsbnifn3vsb4pfaia3nlvgvw6ndpxp7lzjrh23qcwid34v")))) (build-system python-build-system) (propagated-inputs (list python-sphinx)) (home-page "https://github.com/guzzle/guzzle_sphinx_theme") (synopsis "Sphinx theme used by Guzzle") (description "This package provides guzzle_sphinx_theme, a theme for the Sphinx documentation system, used by @uref{http://docs.guzzlephp.org, Guzzle} and several other projects.") (license license:expat))) (define-public python-mpl-sphinx-theme (package (name "python-mpl-sphinx-theme") (version "3.5.0") (source (origin (method url-fetch) (uri (pypi-uri "mpl_sphinx_theme" version)) (sha256 (base32 "0ilsw6s5hfvjzqs3258c8gmg5v3dwa6k69mwmkxsyh1qmv15krpw")))) (build-system python-build-system) (propagated-inputs (list python-pydata-sphinx-theme)) (home-page "https://github.com/matplotlib/mpl-sphinx-theme") (synopsis "Matplotlib theme for Sphinx") (description "This package provides a Matplotlib theme for Sphinx.") (license license:bsd-3))) (define-public python-myst-parser (package (name "python-myst-parser") (version "0.18.1") (source (origin (method git-fetch) ;for tests (uri (git-reference (url "https://github.com/executablebooks/MyST-Parser") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "0lcz9vvy8hbp6cjmbslrlxn3pinf98jykiq8nx5lw5y0lz0mj162")))) (build-system pyproject-build-system) (arguments ;; There are 3 test failures, seemingly due to expecting a slightly ;; different output from Sphinx (see: ;; https://github.com/executablebooks/MyST-Parser/issues/645). (list #:test-flags #~(list "-k" (string-append "not test_basic " "and not test_gettext_html " "and not test_fieldlist_extension")))) (native-inputs (list python-beautifulsoup4 python-docutils python-flit-core python-pytest python-pytest-param-files python-pytest-regressions python-sphinx python-sphinx-pytest)) (propagated-inputs (list python-docutils python-jinja2 python-linkify-it-py python-markdown-it-py python-linkify-it-py python-mdit-py-plugins python-pyyaml python-sphinx python-typing-extensions)) (home-page "https://myst-parser.readthedocs.io/en/latest/") (synopsis "Sphinx and Docutils extension to parse MyST") (description "This package provides a Sphinx and Docutils extension to parse MyST, a rich and extensible flavour of Markdown for authoring technical and scientific documentation.") (license license:expat))) (define-public python-sphinx-rtd-theme (package (name "python-sphinx-rtd-theme") (version "1.0.0") (source (origin (method url-fetch) (uri (pypi-uri "sphinx_rtd_theme" version)) (sha256 (base32 "0p3abj91c3l72ajj5jwblscsdf1jflrnn0djx2h5y6f2wjbx9ipf")))) (build-system python-build-system) (arguments '(#:tests? #f)) ; No tests. (propagated-inputs (list python-docutils python-sphinx)) (home-page "https://github.com/snide/sphinx_rtd_theme/") (synopsis "ReadTheDocs.org theme for Sphinx") (description "A theme for Sphinx used by ReadTheDocs.org.") (license license:expat))) (define-public python-breathe (package (name "python-breathe") (version "4.35.0") (source (origin (method git-fetch) ;git repo has tests (uri (git-reference (url "https://github.com/breathe-doc/breathe") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1hlcrhr533yjkz9ds83xnmn8h6z3r6vfzz7qrpy14n9j4ysyz59c")))) (build-system python-build-system) (arguments (list #:phases #~(modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (with-directory-excursion "tests" (invoke "python" "-m" "pytest" "-v")))))))) (native-inputs (list python-pytest)) (propagated-inputs (list python-docutils python-sphinx)) (home-page "https://www.breathe-doc.org") (synopsis "ReStructuredText and Sphinx bridge to Doxygen") (description "This package is an extension to reStructuredText and Sphinx to be able to read and render the Doxygen xml output.") (license license:bsd-3))) (define-public python-sphinx-intl (package (name "python-sphinx-intl") (version "2.0.1") (source (origin (method url-fetch) (uri (pypi-uri "sphinx-intl" version)) (sha256 (base32 "1d1q0sanjp4nkfvhsxi75zf3xjyyi8nzxvl3v7l0jy9ld70nwnmj")))) (build-system python-build-system) (propagated-inputs (list python-sphinx python-click)) (home-page "https://github.com/sphinx-doc/sphinx-intl") (synopsis "Sphinx utility that makes it easy to translate and to apply translation") (description "A utility tool that provides several features that make it easy to translate and to apply translation to Sphinx generated document.") (license license:bsd-2))) (define-public python-sphinxext-opengraph (package (name "python-sphinxext-opengraph") (version "0.6.3") (source (origin (method git-fetch) ; no tests in PyPI release (uri (git-reference (url "https://github.com/wpilibsuite/sphinxext-opengraph") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1wrgpan9z65fv4hbvisz4sypc4w5ammnxkyn5lhr43wdr6b967k1")))) (build-system python-build-system) (arguments `(#:phases (modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest" "-vv"))))))) (native-inputs (list python-beautifulsoup4 python-pytest python-sphinx)) (home-page "https://github.com/wpilibsuite/sphinxext-opengraph") (synopsis "Sphinx Extension to enable OpenGraph support") (description "This package provides a Sphinx Extension to generate OG metadata.") (license license:bsd-3))) (define-public python-sphinx-autobuild (package (name "python-sphinx-autobuild") (version "2021.3.14") (source (origin (method url-fetch) (uri (pypi-uri "sphinx-autobuild" version)) (sha256 (base32 "019z8kvnaw11r41b6pfdy9iz4iwyr0s51hs0a5djn797dsva676y")))) (build-system python-build-system) (arguments '(#:phases (modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest" "-vv"))))))) (propagated-inputs (list python-colorama python-livereload python-sphinx)) (native-inputs (list python-pytest)) (home-page "https://github.com/GaretJax/sphinx-autobuild") (synopsis "Rebuild Sphinx documentation when a change is detected") (description "This package lets you watch a Sphinx directory and rebuild the documentation when a change is detected. It also includes a livereload enabled web server.") (license license:expat))) (define-public python-sphinx-autodoc-typehints (package (name "python-sphinx-autodoc-typehints") (version "1.18.3") (source (origin (method git-fetch) ;no tests in pypi archive (uri (git-reference (url "https://github.com/tox-dev/sphinx-autodoc-typehints") (commit version))) (file-name (git-file-name name version)) (sha256 (base32 "049dlay21f4bccig31fkbzq2m8v0h6g63p1cn3dxay9q3h0mzgs0")))) (build-system python-build-system) (arguments (list #:phases #~(modify-phases %standard-phases (add-before 'build 'pretend-version ;; The version string is usually derived via setuptools-scm, but ;; without the git metadata available, the version string is set to ;; '0.0.0'. (lambda _ (setenv "SETUPTOOLS_SCM_PRETEND_VERSION" #$version))) (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest" "-vv" "tests" ;; This test requires to download an objects.inv file ;; from the Sphinx website. "-k" "not test_format_annotation"))))))) (propagated-inputs (list python-sphinx)) (native-inputs (list python-nptyping python-pytest python-setuptools-scm python-sphobjinv python-typing-extensions)) (home-page "https://pypi.org/project/sphinx-autodoc-typehints/") (synopsis "Type hints for the Sphinx autodoc extension") (description "This extension allows you to use Python 3 annotations for documenting acceptable argument types and return value types of functions.") (license license:expat))) (define-public python-sphinx-pytest (package (name "python-sphinx-pytest") (version "0.0.5") (source (origin (method url-fetch) (uri (pypi-uri "sphinx_pytest" version)) (sha256 (base32 "13d3psm5vyb8rdj0mhnpn5m09k8xdaszcxdpng52fpz9sw8pngk7")))) (build-system pyproject-build-system) (native-inputs (list python-flit-core)) (propagated-inputs (list python-pytest python-sphinx)) (home-page "https://github.com/chrisjsewell/sphinx-pytest") (synopsis "Pytest fixtures for Sphinx extensions") (description "This Pytest extension mainly provides some Pytest fixtures to simulate converting some source text to Docutils @acronym{AST, Abstract Syntax Tree} at different stages: before transforms, after transforms, etc.") (license license:expat))) (define-public python-nbsphinx (package (name "python-nbsphinx") (version "0.8.8") (source (origin (method url-fetch) (uri (pypi-uri "nbsphinx" version)) (sha256 (base32 "1v1lzkfx2lslhslqb110zxmm4dmdg6hs2rahf713c2rk9f10q2dm")))) (build-system python-build-system) (propagated-inputs (list python-docutils python-jinja2 python-nbconvert python-nbformat python-sphinx python-traitlets)) (home-page "https://nbsphinx.readthedocs.io/") (synopsis "Jupyter Notebook Tools for Sphinx") (description "@code{python-nbsphinx} is a Sphinx extension that provides a source parser for @code{*.ipynb} files. Custom Sphinx directives are used to show Jupyter Notebook code cells (and of course their results) in both HTML and LaTeX output. Un-evaluated notebooks - i.e. notebooks without stored output cells - will be automatically executed during the Sphinx build process.") (license license:expat))) (define-public python-sphobjinv (package (name "python-sphobjinv") (version "2.0.1") (source (origin (method url-fetch) (uri (pypi-uri "sphobjinv" version)) (sha256 (base32 "126lgm54c94ay3fci512ap4l607gak90pbz0fk98syxvj5izrrzx")))) (build-system python-build-system) (propagated-inputs (list python-attrs python-certifi python-fuzzywuzzy python-jsonschema python-levenshtein)) (home-page "https://github.com/bskinn/sphobjinv") (synopsis "Sphinx cross-reference tool") (description "Sphinx objects.inv inspection/manipulation tool.") (license license:expat))) (define-public python-jupyter-sphinx (package (name "python-jupyter-sphinx") (version "0.3.2") (source (origin ;; Pypi tarball doesn't contain tests. (method git-fetch) (uri (git-reference (url "https://github.com/jupyter/jupyter-sphinx") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "0bsb17vzbgvrzvh87pi88b157hyigdwnf1lhrgvan03i2300h15c")))) (build-system python-build-system) (arguments (list #:phases #~(modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest"))))))) (propagated-inputs (list python-ipython python-ipywidgets python-nbconvert python-nbformat)) (native-inputs (list python-pytest python-sphinx)) (home-page "https://github.com/jupyter/jupyter-sphinx/") (synopsis "Jupyter Sphinx Extensions") (description "Jupyter-sphinx is a Sphinx extension that executes embedded code in a Jupyter kernel, and embeds outputs of that code in the document. It has support for rich output such as images, LaTeX math and even JavaScript widgets, and supports thebelab for live code execution with minimal effort.") (license license:bsd-3))) (define-public python-sphinxcontrib-autoprogram (package (name "python-sphinxcontrib-autoprogram") (version "0.1.7") (source (origin (method url-fetch) (uri (pypi-uri "sphinxcontrib-autoprogram" version)) (sha256 (base32 "06hzim0d3fd72kf30fyjbbm5n8ibyybic0kf62gm79qp50zjwr5w")))) (build-system python-build-system) (propagated-inputs (list python-six)) (native-inputs (list python-sphinx)) (home-page "https://github.com/sphinx-contrib/autoprogram") (synopsis "Documenting CLI programs") (description "This Sphinx extension, @code{sphinxcontrib.autoprogram}, provides an automated way to document command-line programs. It scans @code{argparse.ArgumentParser} object, and then expands it into a set of @code{.. program::} and @code{.. option::} directives.") (license license:bsd-2))) (define-public python-sphinx-theme-builder (package (name "python-sphinx-theme-builder") (version "0.2.0b1") (source (origin (method git-fetch) ;no tests in pypi archive (uri (git-reference (url "https://github.com/pradyunsg/sphinx-theme-builder") (commit version))) (file-name (git-file-name name version)) (sha256 (base32 "15gvwzd4l3wcmd6fns8xvv44yzxmamr1nfn28mp12sdw2y10v2ba")))) (build-system python-build-system) (arguments (list #:phases #~(modify-phases %standard-phases ;; XXX: PEP 517 manual build copied from python-isort. (replace 'build (lambda _ ;; ZIP does not support timestamps before 1980. (setenv "SOURCE_DATE_EPOCH" "315532800") (invoke "python" "-m" "build" "--wheel" "--no-isolation" "."))) (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest" "-vv")))) (replace 'install (lambda _ (let ((whl (car (find-files "dist" "\\.whl$")))) (invoke "pip" "--no-cache-dir" "--no-input" "install" "--no-deps" "--prefix" #$output whl))))))) (native-inputs (list python-flit-core python-pytest)) (propagated-inputs (list python-pypa-build python-click python-nodeenv python-packaging python-pyproject-metadata python-rich python-sphinx-autobuild python-tomli)) (home-page "https://github.com/pradyunsg/sphinx-theme-builder") (synopsis "Tool for authoring Sphinx themes") (description "This package provides a tool for authoring Sphinx themes with a simple (opinionated) workflow.") (license license:expat))) (define-public python-sphinx-sitemap (package (name "python-sphinx-sitemap") (version "2.2.0") (source (origin (method url-fetch) (uri (pypi-uri "sphinx-sitemap" version)) (sha256 (base32 "0dvpryrz7vn8rvayzy5nrmqy4wyzlaxcx88bl46prc9w4cwxmbb5")))) (build-system python-build-system) (propagated-inputs (list python-sphinx)) (home-page "https://github.com/jdillard/sphinx-sitemap") (synopsis "Sitemap generator for Sphinx") (description "A Sphinx extension to generate multiversion and multilanguage sitemaps.org compliant sitemaps for the HTML version of your Sphinx documentation.") (license license:expat))) (define-public python-pydata-sphinx-theme (package (name "python-pydata-sphinx-theme") ;; TODO: This is not the latest release, but the 0.8.x series introduced a ;; new Sphinx theme build system that complicate things (see: ;; https://github.com/pydata/pydata-sphinx-theme/issues/628 and ;; https://src.fedoraproject.org/rpms/python-pydata-sphinx-theme ;; /blob/rawhide/f/prepare_vendor.sh). (version "0.7.2") (source (origin (method url-fetch) (uri (pypi-uri "pydata-sphinx-theme" version)) (sha256 (base32 "0ph69bnnw9w8vksc7rk45q5yknsrsgk9a19xsbxym46jrmgz67b7")))) (build-system python-build-system) (arguments (list #:phases #~(modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "pytest" "-vv"))))))) (propagated-inputs (list python-beautifulsoup4 python-docutils python-jinja2 python-sphinx)) (native-inputs (list python-pytest python-pytest-regressions)) (home-page "https://github.com/pydata/pydata-sphinx-theme") (synopsis "Bootstrap-based Sphinx theme") (description "This package provides a Bootstrap-based Sphinx theme from the PyData community.") (license license:bsd-3)))